<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>xmake</title>
  <link rel="icon" href="/assets/img/favicon.ico">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <meta name="description" content="Description">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <link href="/assets/npm/github-markdown/github-markdown.min.css" rel="stylesheet">
  <style>
	.markdown-body {
		box-sizing: border-box;
		min-width: 200px;
		max-width: 980px;
		margin: 0 auto;
		padding: 45px;
	}

	@media (max-width: 767px) {
		.markdown-body {
			padding: 15px;
		}
	}
  </style>
</head>
<body>
<article class="markdown-body">
<h4>This is a mirror page, please see the original page: </h4><a href="https://xmake.io/#/zh-cn/manual/project_target">https://xmake.io/#/zh-cn/manual/project_target</a>
<div id="wwads-panel" class="wwads-cn wwads-vertical wwads-sticky" data-id="239" style="max-width:180px;bottom:20px;right:20px;width:200px;height:260px;background:#fff;position:fixed"></div>
</br>
    <script type="text/javascript" charset="UTF-8" src="https://cdn.wwads.cn/js/makemoney.js" async></script>
<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CE7I52QU&placement=xmakeio" id="_carbonads_js"></script>
<style>
#carbonads {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu,
  Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
}

#carbonads {
  display: flex;
  max-width: 330px;
  background-color: hsl(0, 0%, 98%);
  box-shadow: 0 1px 4px 1px hsla(0, 0%, 0%, .1);
}

#carbonads a {
  color: inherit;
  text-decoration: none;
}

#carbonads a:hover {
  color: inherit;
}

#carbonads span {
  position: relative;
  display: block;
  overflow: hidden;
}

#carbonads .carbon-wrap {
  display: flex;
}

.carbon-img {
  display: block;
  margin: 0;
  line-height: 1;
}

.carbon-img img {
  display: block;
}

.carbon-text {
  font-size: 13px;
  padding: 10px;
  line-height: 1.5;
  text-align: left;
}

.carbon-poweredby {
  display: block;
  padding: 8px 10px;
  background: repeating-linear-gradient(-45deg, transparent, transparent 5px, hsla(0, 0%, 0%, .025) 5px, hsla(0, 0%, 0%, .025) 10px) hsla(203, 11%, 95%, .4);
  text-align: center;
  text-transform: uppercase;
  letter-spacing: .5px;
  font-weight: 600;
  font-size: 9px;
  line-height: 1;
}
</style>
    <p>定义和设置子工程模块，每个<code>target</code>对应一个子工程，最后会生成一个目标程序，有可能是可执行程序，也有可能是库模块。</p>
<p>!> target的接口，都是可以放置在target外面的全局作用域中的，如果在全局中设置，那么会影响所有子工程target。</p>
<p>例如：</p>
<pre><code class="lang-lua">-- 会同时影响test和test2目标
add_defines("DEBUG")

target("test")
    add_files("*.c")

target("test2")
    add_files("*.c")
</code></pre>
<p>!> <code>target</code>域是可以重复进入来实现分离设置的。</p>
<h3 id="target">target</h3>
<h4 id="">定义工程目标</h4>
<p>定义一个新的控制台工程目标，工程名为<code>test</code>，最后生成的目标名也是<code>test</code>。</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("src/*.c")
</code></pre>
<p>可以重复调用这个api，进入target域修改设置</p>
<pre><code class="lang-lua">-- 定义目标demo，并进入demo设置模式
target("demo")
    set_kind("binary")
    add_files("src/demo.c")

-- 定义和设置其他目标
target("other")
    ...

-- 重新进入demo目标域，添加test.c文件
target("demo")
    add_files("src/test.c")
</code></pre>
<p><p class="tip"><br>所有根域的设置，会全局影响所有target目标，但是不会影响option的定义。<br></p>

</p>
<pre><code class="lang-lua">-- 在根域对所有target添加-DDEBUG的宏定义，影响所有target（demo和test都会加上此宏定义）
add_defines("DEBUG")

target("demo")
    set_kind("binary")
    add_files("src/demo.c")

target("test")
    set_kind("binary")
    add_files("src/test.c")
</code></pre>
<h3 id="target_end">target_end</h3>
<h4 id="">结束定义工程目标</h4>
<p>这是一个可选的api，如果不调用，那么<code>target("xxx")</code>之后的所有设置都是针对这个target进行的，除非进入其他<code>target</code>, <code>option</code>, <code>task</code>域。</p>
<p>如果想设置完当前<code>target</code>后，显示离开<code>target</code>域，进入根域设置，那么可以通过这个api才操作，例如：</p>
<pre><code class="lang-lua">target("test")
    set_kind("static")
    add_files("src/*.c")
target_end()

-- 此处已在根域
-- ...
</code></pre>
<p>如果不调用这个api的话:</p>
<pre><code class="lang-lua">target("test")
    set_kind("static")
    add_files("src/*.c")

-- 此处还在上面target域中，之后的设置还是针对test进行的设置
-- ...

-- 这个时候才离开test，进入另外一个target域中
target("test2")
    ...
</code></pre>
<h3 id="targetset_kind">target:set_kind</h3>
<h4 id="">设置目标编译类型</h4>
<p>设置目标类型，目前支持的类型有：</p>
<table>
<thead>
<tr>
<th>值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>phony</td>
<td>假的目标程序</td>
</tr>
<tr>
<td>binary</td>
<td>二进制程序</td>
</tr>
<tr>
<td>static</td>
<td>静态库程序</td>
</tr>
<tr>
<td>shared</td>
<td>动态库程序</td>
</tr>
<tr>
<td>object</td>
<td>仅仅编译对象集合</td>
</tr>
<tr>
<td>headeronly</td>
<td>仅仅头文件集合</td>
</tr>
</tbody>
</table>
<h5 id="binary">binary</h5>
<ul>
<li>可执行文件类型</li>
</ul>
<pre><code class="lang-lua">target("demo")
    set_kind("binary")
    add_files("src/*.c")
</code></pre>
<p>!> 2.5.5 开始，如果没有设置 set_kind 接口，默认就是 binary 类型。</p>
<p>所以我们简化为：</p>
<pre><code class="lang-lua">target("demo")
    add_files("src/*.c")
</code></pre>
<p>甚至:</p>
<pre><code class="lang-lua">target("demo", {files = "src/*.c"})
</code></pre>
<h5 id="static">static</h5>
<ul>
<li>静态库目标类型</li>
</ul>
<pre><code class="lang-lua">target("demo")
    set_kind("static")
    add_files("src/*.c")
</code></pre>
<h5 id="shared">shared</h5>
<ul>
<li>动态库目标类型</li>
</ul>
<pre><code class="lang-lua">target("demo")
    set_kind("shared")
    add_files("src/*.c")
</code></pre>
<h5 id="object">object</h5>
<ul>
<li>纯对象文件列表类型</li>
</ul>
<p>通常用于两个目标程序间，部分对象文件共享，仅仅编译一次。也可以用于分离对象文件列表，配置不同的编译参数。</p>
<h5 id="phony">phony</h5>
<ul>
<li>空目标类型</li>
</ul>
<p>它是一个特殊的目标程序类型，它不生成任何实际的程序文件，仅仅用于组合其他目标程序的依赖关系。</p>
<pre><code class="lang-lua">target("test1")
    set_kind("binary")
    add_files("src/*.c")

target("test2")
    set_kind("binary")
    add_files("src/*.c")

target("demo")
    set_kind("phony")
    add_deps("test1", "test2")
</code></pre>
<p>比如上述配置，我们就可以在执行 <code>xmake build demo</code> 编译的时候，同时编译相关的两个依赖程序：test1和test2。</p>
<h5 id="headeronly">headeronly</h5>
<ul>
<li>纯头文件目标类型</li>
</ul>
<p>2.5.9 之后，我们新增了 <code>headeronly</code> 目标类型，这个类型的目标程序，我们不会实际编译它们，因为它没有源文件需要被编译。</p>
<p>但是它包含了头文件列表，这通常用于 headeronly 库项目的安装，IDE 工程的文件列表生成，以及安装阶段的 cmake/pkgconfig 导入文件的生成。</p>
<p>例如：</p>
<pre><code class="lang-lua">add_rules("mode.release", "mode.debug")

target("foo")
    set_kind("headeronly")
    add_headerfiles("src/foo.h")
    add_rules("utils.install.cmake_importfiles")
    add_rules("utils.install.pkgconfig_importfiles")
</code></pre>
<p>更多详情见：<a href="https://github.com/xmake-io/xmake/issues/1747">#1747</a></p>
<h3 id="targetset_strip">target:set_strip</h3>
<h4 id="strip">设置是否strip信息</h4>
<p>设置当前目标的strip模式，目前支持一下模式：</p>
<table>
<thead>
<tr>
<th>值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>debug</td>
<td>链接的时候，strip掉调试符号</td>
</tr>
<tr>
<td>all</td>
<td>链接的时候，strip掉所有符号，包括调试符号</td>
</tr>
</tbody>
</table>
<p>这个api一般在release模式下使用，可以生成更小的二进制程序。。</p>
<pre><code class="lang-lua">target("xxxx")
    set_strip("all")
</code></pre>
<p><p class="tip"><br>这个api不一定非得在target之后使用，如果没有target指定，那么将会设置到全局模式。。<br></p>

</p>
<h3 id="targetset_enabled">target:set_enabled</h3>
<h4 id="">设置是否启用或禁用目标</h4>
<p>如果设置<code>set_enabled(false)</code>，则会直接禁用对应的target，包括target的加载和信息获取，而<a href="#targetset_default">set_default</a>仅仅只是设置默认不去编译，但是target还是能获取到相关信息的，默认也会被加载。</p>
<h3 id="targetset_default">target:set_default</h3>
<h4 id="">设置是否为默认构建安装目标</h4>
<p>这个接口用于设置给定工程目标是否作为默认构建，如果没有调用此接口进行设置，那么这个目标就是默认被构建的，例如：</p>
<pre><code class="lang-lua">target("test1")
    set_default(false)

target("test2")
    set_default(true)

target("test3")
    ...
</code></pre>
<p>上述代码的三个目标，在执行<code>xmake</code>, <code>xmake install</code>, <code>xmake package</code>, <code>xmake run</code>等命令的时候，如果不指定目标名，那么：</p>
<table>
<thead>
<tr>
<th>目标名</th>
<th>行为</th>
</tr>
</thead>
<tbody>
<tr>
<td>test1</td>
<td>不会被默认构建、安装、打包和运行</td>
</tr>
<tr>
<td>test2</td>
<td>默认构建、安装、打包和运行</td>
</tr>
<tr>
<td>test3</td>
<td>默认构建、安装、打包和运行</td>
</tr>
</tbody>
</table>
<p>通过上面的例子，可以看到默认目标可以设置多个，运行的时候也会依次运行。</p>
<p><p class="tip"><br>    需要注意的是，<code>xmake uninstall</code>和<code>xmake clean</code>命令不受此接口设置影响，因为用户大部分情况下都是喜欢清除和卸载所有。<br></p>

</p>
<p>如果不想使用默认的目标，那么可以手动指定需要构建安装的目标：</p>
<pre><code class="lang-bash">$ xmake build targetname
$ xmake install targetname
</code></pre>
<p>如果要强制构建安装所有目标，可以传入<code>[-a|--all]</code>参数：</p>
<pre><code class="lang-bash">$ xmake build [-a|--all]
$ xmake install [-a|--all]
</code></pre>
<h3 id="targetset_options">target:set_options</h3>
<h4 id="">设置关联选项</h4>
<p>添加选项依赖，如果通过<a href="#option">option</a>接口自定义了一些选项，那么只有在指定<code>target</code>目标域下，添加此选项，才能进行关联生效。</p>
<pre><code class="lang-lua">-- 定义一个hello选项
option("hello")
    set_default(false)
    set_showmenu(true)
    add_defines("HELLO_ENABLE")

target("test")
    -- 如果hello选项被启用了，这个时候就会将-DHELLO_ENABLE宏应用到test目标上去
    set_options("hello")
</code></pre>
<p><p class="warn"><br>只有调用<code>set_options</code>进行关联生效后，<a href="#option">option</a> 中定义的一些设置才会影响到此<code>target</code>目标，例如：宏定义、链接库、编译选项等等<br></p>

</p>
<h3 id="targetset_symbols">target:set_symbols</h3>
<h4 id="">设置符号信息</h4>
<p>设置目标的符号模式，如果当前没有定义target，那么将会设置到全局状态中，影响所有后续的目标。</p>
<p>目前主要支持一下几个级别：</p>
<table>
<thead>
<tr>
<th>值</th>
<th>描述</th>
<th>gcc/clang</th>
<th>msvc</th>
</tr>
</thead>
<tbody>
<tr>
<td>debug</td>
<td>添加调试符号</td>
<td>-g</td>
<td>/Zi /Pdxxx.pdb</td>
</tr>
<tr>
<td>debug, edit</td>
<td>仅 msvc 生效，配合debug级别使用</td>
<td>忽略</td>
<td>/ZI /Pdxxx.pdb</td>
</tr>
<tr>
<td>debug, embed</td>
<td>仅 msvc 生效，配合debug级别使用</td>
<td>忽略</td>
<td>/Z7</td>
</tr>
<tr>
<td>hidden</td>
<td>设置符号不可见</td>
<td>-fvisibility=hidden</td>
<td>忽略</td>
</tr>
</tbody>
</table>
<p>这两个值也可以同时被设置，例如：</p>
<pre><code class="lang-lua">-- 添加调试符号, 设置符号不可见
set_symbols("debug", "hidden")
</code></pre>
<p>如果没有调用这个api，默认是禁用调试符号的。。</p>
<p>!> 在v2.3.3以上版本，通过跟<code>set_strip("all")</code>配合同时设置，可以自动生成独立的调试符号，例如对于ios程序，就是.dSYM文件，对于android等其他程序，就是.sym符号文件。</p>
<p>如果target同时设置了下面两个设置，就会启用符号文件生成</p>
<pre><code class="lang-lua">target("test")
    set_symbols("debug")
    set_strip("all")
</code></pre>
<p>对于内置的release模式，默认不启用符号生成，仅仅只是strip targetfile，如果要启用，只需要再额外开启debug符号就行，因为mode.release内部默认已经启用了strip了。</p>
<pre><code class="lang-lua">add_rules("mode.release")
target("test")
    set_symbols("debug")
</code></pre>
<p>ios程序会生成.dSYM文件，然后同时Strip自身符号</p>
<pre><code class="lang-console">[ 62%]: linking.release libtest.dylib
[ 62%]: generating.release test.dSYM
</code></pre>
<p>android程序会生成.sym文件（其实就是带符号的so/binary程序），然后同时Strip自身符号</p>
<pre><code class="lang-console">[ 62%]: linking.release libtest.so
[ 62%]: generating.release test.sym
</code></pre>
<p>v2.3.9 以上版本，新增了 <code>edit</code> 和 <code>embed</code> 两个额外的附属级别，需要组合 <code>debug</code> 级别一起使用，仅用于进一步细分 msvc 编译器的调试符号格式，例如：</p>
<pre><code class="lang-lua">set_symbols("debug", "edit")
</code></pre>
<p>会从默认的 <code>-Zi -Pdxxx.pdb</code> 切换到 <code>-ZI -Pdxxx.pdb</code> 编译选项，开启 <code>Edit and Continue</code> 调试符号格式信息，当然这并不会影响 gcc/clang 的处理，所以也是完全兼容的。</p>
<h3 id="targetset_basename">target:set_basename</h3>
<h4 id="">设置目标文件名</h4>
<p>默认情况下，生成的目标文件名基于<code>target("name")</code>中配置的值，例如：</p>
<pre><code class="lang-lua">-- 目标文件名为：libxxx.a
target("xxx")
    set_kind("static")

-- 目标文件名为：libxxx2.so
target("xxx2")
    set_kind("shared")
</code></pre>
<p>默认的命名方式，基本上可以满足大部分情况下的需求，但是如果有时候想要更加定制化目标文件名</p>
<p>例如，按编译模式和架构区分目标名，这个时候可以使用这个接口，来设置：</p>
<pre><code class="lang-lua">target("xxx")
    set_kind("static")
    set_basename("xxx_$(mode)_$(arch)")
</code></pre>
<p>如果这个时候，编译配置为：<code>xmake f -m debug -a armv7</code>，那么生成的文件名为：<code>libxxx_debug_armv7.a</code></p>
<p>如果还想进一步定制目标文件的目录名，可参考：<a href="#targetset_targetdir">set_targetdir</a>。</p>
<p>或者通过编写自定义脚本，实现更高级的逻辑，具体见：<a href="#targetafter_build">after_build</a>和<a href="/mirror/zh-cn/manual/builtin_modules.html#osmv">os.mv</a>。</p>
<h3 id="targetset_filename">target:set_filename</h3>
<h4 id="">设置目标文件全名</h4>
<p>它跟<a href="#targetset_basename">set_basename</a>的区别在于，<a href="#targetset_basename">set_basename</a>设置名字不带后缀跟前缀，例如：<code>libtest.a</code>，basename如果改成test2后就变成了<code>libtest2.a</code>。</p>
<p>而filename的修改，是修改整个目标文件名，包括前后缀，例如可以直接把<code>libtest.a</code>改成<code>test.dll</code>，这个对于<a href="#targetset_basename">set_basename</a>是做不到的。</p>
<h3 id="targetset_prefixname">target:set_prefixname</h3>
<h4 id="">设置目标文件的前置名</h4>
<p>2.5.5 之后版本才支持，可以修改设置目标文件的前置名，例如将默认的：<code>libtest.so</code> 改成 <code>test.so</code></p>
<pre><code class="lang-lua">target("test")
    set_prefixname("")
</code></pre>
<h3 id="targetset_suffixname">target:set_suffixname</h3>
<h4 id="">设置目标文件的后置名</h4>
<p>2.5.5 之后版本才支持，可以修改设置目标文件的后置名，例如将默认的：<code>libtest.so</code> 改成 <code>libtest-d.so</code></p>
<pre><code class="lang-lua">target("test")
    set_suffixname("-d")
</code></pre>
<h3 id="targetset_extension">target:set_extension</h3>
<h4 id="">设置目标文件的扩展名</h4>
<p>2.5.5 之后版本才支持，可以修改设置目标文件的扩展名，例如将默认的：<code>libtest.so</code> 改成 <code>test.dll</code></p>
<pre><code class="lang-lua">target("test")
    set_prefixname("")
    set_extension(".dll")
</code></pre>
<h3 id="targetset_warnings">target:set_warnings</h3>
<h4 id="">设置警告级别</h4>
<p>设置当前目标的编译的警告级别，一般支持一下几个级别：</p>
<table>
<thead>
<tr>
<th>值</th>
<th>描述</th>
<th>gcc/clang</th>
<th>msvc</th>
</tr>
</thead>
<tbody>
<tr>
<td>none</td>
<td>禁用所有警告</td>
<td>-w</td>
<td>-W0</td>
</tr>
<tr>
<td>less</td>
<td>启用较少的警告</td>
<td>-W1</td>
<td>-W1</td>
</tr>
<tr>
<td>more</td>
<td>启用较多的警告</td>
<td>-W3</td>
<td>-W3</td>
</tr>
<tr>
<td>extra</td>
<td>启用额外警告</td>
<td>-Wextra</td>
<td></td>
</tr>
<tr>
<td>pedantic</td>
<td>启用非语言标准的使用警告</td>
<td>-Wpedantic</td>
<td></td>
</tr>
<tr>
<td>all</td>
<td>启用所有警告</td>
<td>-Wall</td>
<td>-W3</td>
</tr>
<tr>
<td>allextra</td>
<td>启用所有警告+额外的警告</td>
<td>-Wall -Wextra</td>
<td>-W4</td>
</tr>
<tr>
<td>everything</td>
<td>启用全部支持的警告</td>
<td>-Wall -Wextra -Weffc++ / -Weverything</td>
<td>-Wall</td>
</tr>
<tr>
<td>error</td>
<td>将所有警告作为编译错误</td>
<td>-Werror</td>
<td>-WX</td>
</tr>
</tbody>
</table>
<p>这个api的参数是可以混合添加的，例如：</p>
<pre><code class="lang-lua">-- 启用所有警告，并且作为编译错误处理
set_warnings("all", "error")
</code></pre>
<p>如果当前没有目标，调用这个api将会设置到全局模式。。</p>
<h3 id="targetset_optimize">target:set_optimize</h3>
<h4 id="">设置优化级别</h4>
<p>设置目标的编译优化等级，如果当前没有设置目标，那么将会设置到全局状态中，影响所有后续的目标。</p>
<p>目前主要支持一下几个级别：</p>
<table>
<thead>
<tr>
<th>值</th>
<th>描述</th>
<th>gcc/clang</th>
<th>msvc</th>
</tr>
</thead>
<tbody>
<tr>
<td>none</td>
<td>禁用优化</td>
<td>-O0</td>
<td>-Od</td>
</tr>
<tr>
<td>fast</td>
<td>快速优化</td>
<td>-O1</td>
<td>default</td>
</tr>
<tr>
<td>faster</td>
<td>更快的优化</td>
<td>-O2</td>
<td>-O2</td>
</tr>
<tr>
<td>fastest</td>
<td>最快运行速度的优化</td>
<td>-O3</td>
<td>-Ox -fp:fast</td>
</tr>
<tr>
<td>smallest</td>
<td>最小化代码优化</td>
<td>-Os</td>
<td>-O1 -GL</td>
</tr>
<tr>
<td>aggressive</td>
<td>过度优化</td>
<td>-Ofast</td>
<td>-Ox -fp:fast</td>
</tr>
</tbody>
</table>
<p>例如：</p>
<pre><code class="lang-lua">-- 最快运行速度的优化
set_optimize("fastest")
</code></pre>
<h3 id="targetset_languages">target:set_languages</h3>
<h4 id="">设置代码语言标准</h4>
<p>设置目标代码编译的语言标准，如果当前没有目标存在，将会设置到全局模式中。。。</p>
<p>支持的语言标准目前主要有以下几个：</p>
<table>
<thead>
<tr>
<th>值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>ansi</td>
<td>c语言标准: ansi</td>
</tr>
<tr>
<td>c89</td>
<td>c语言标准: c89</td>
</tr>
<tr>
<td>gnu89</td>
<td>c语言标准: gnu89</td>
</tr>
<tr>
<td>c99</td>
<td>c语言标准: c99</td>
</tr>
<tr>
<td>gnu99</td>
<td>c语言标准: gnu99</td>
</tr>
<tr>
<td>c11</td>
<td>c语言标准: c11</td>
</tr>
<tr>
<td>c17</td>
<td>c语言标准: c17</td>
</tr>
<tr>
<td>clatest</td>
<td>c语言标准: clatest</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>cxx98</td>
<td>c++语言标准: <code>c++98</code></td>
</tr>
<tr>
<td>gnuxx98</td>
<td>c++语言标准: <code>gnu++98</code></td>
</tr>
<tr>
<td>cxx11</td>
<td>c++语言标准: <code>c++11</code></td>
</tr>
<tr>
<td>gnuxx11</td>
<td>c++语言标准: <code>gnu++11</code></td>
</tr>
<tr>
<td>cxx14</td>
<td>c++语言标准: <code>c++14</code></td>
</tr>
<tr>
<td>gnuxx14</td>
<td>c++语言标准: <code>gnu++14</code></td>
</tr>
<tr>
<td>cxx1z</td>
<td>c++语言标准: <code>c++1z</code></td>
</tr>
<tr>
<td>gnuxx1z</td>
<td>c++语言标准: <code>gnu++1z</code></td>
</tr>
<tr>
<td>cxx17</td>
<td>c++语言标准: <code>c++17</code></td>
</tr>
<tr>
<td>gnuxx17</td>
<td>c++语言标准: <code>gnu++17</code></td>
</tr>
<tr>
<td>cxx20</td>
<td>c++语言标准: <code>c++20</code></td>
</tr>
<tr>
<td>gnuxx20</td>
<td>c++语言标准: <code>gnu++20</code></td>
</tr>
<tr>
<td>cxxlatest</td>
<td>c++语言标准: <code>c++latest</code></td>
</tr>
<tr>
<td>gnuxxlatest</td>
<td>c++语言标准: <code>gnu++latest</code></td>
</tr>
</tbody>
</table>
<p>c标准和c++标准可同时进行设置，例如：</p>
<pre><code class="lang-lua">-- 设置c代码标准：c99， c++代码标准：c++11
set_languages("c99", "cxx11")
</code></pre>
<p>并不是设置了指定的标准，编译器就一定会按这个标准来编译，毕竟每个编译器支持的力度不一样，但是xmake会尽最大可能的去适配当前编译工具的支持标准。</p>
<p>msvc 的编译器并不支持按 c99 的标准来编译c代码，只能支持到c89，但是xmake为了尽可能的支持它，所以在设置c99的标准后，xmake会强制按c++代码模式去编译c代码，从一定程度上解决了windows下编译c99的c代码问题。。<br>用户不需要去额外做任何修改。</p>
<p>不过最新的 msvc 编译已经支持上了 c11/c17 标准，xmake 也就不会再做额外的特殊处理。</p>
<h3 id="targetset_fpmodels">target:set_fpmodels</h3>
<h4 id="floatpoint">设置float-point编译模式</h4>
<p>此接口用于设置浮点的编译模式，对数学计算相关优化的编译抽象设置，提供：fast, strict, except, precise 等几种常用的级别，有些可同时设置，有些是有冲突的，最后设置的生效。</p>
<p>关于这些级别的说明，可以参考下微软的文档：<a href="https://docs.microsoft.com/en-us/cpp/build/reference/fp-specify-floating-point-behavior?view=vs-2019">Specify floating-point behavior</a></p>
<p>当然，对应gcc/icc等其他编译器，xmake 会映射到不同的编译flags。</p>
<pre><code class="lang-lua">set_fpmodels("fast")
set_fpmodels("strict")
set_fpmodels("fast", "except")
set_fpmodels("precise") -- default
</code></pre>
<p>关于这块详情见：<a href="https://github.com/xmake-io/xmake/issues/981">https://github.com/xmake-io/xmake/issues/981</a></p>
<h3 id="targetset_targetdir">target:set_targetdir</h3>
<h4 id="">设置生成目标文件目录</h4>
<p>设置目标程序文件的输出目录，一般情况下，不需要设置，默认会输出在build目录下</p>
<p>而build的目录可以在工程配置的时候，手动修改：</p>
<pre><code class="lang-bash">xmake f -o /tmp/build
</code></pre>
<p>修改成<code>/tmp/build</code>后，目标文件默认输出到<code>/tmp/build</code>下面。</p>
<p>而如果用这个接口去设置，就不需要每次敲命令修改了，例如：</p>
<pre><code class="lang-lua">target("test")
    set_targetdir("/tmp/build")
</code></pre>
<p><p class="tip"><br>如果显示设置了<code>set_targetdir</code>， 那么优先选择<code>set_targetdir</code>指定的目录为目标文件的输出目录。<br></p>

</p>
<h3 id="targetset_objectdir">target:set_objectdir</h3>
<h4 id="">设置对象文件生成目录</h4>
<p>设置目标target的对象文件(<code>*.o/obj</code>)的输出目录，例如:</p>
<pre><code class="lang-lua">target("test")
    set_objectdir("$(buildir)/.objs")
</code></pre>
<h3 id="targetset_dependir">target:set_dependir</h3>
<h4 id="">设置依赖文件生成目录</h4>
<p>设置目标target的编译依赖文件(<code>.deps</code>)的输出目录，例如:</p>
<pre><code class="lang-lua">target("test")
    set_dependir("$(buildir)/.deps")
</code></pre>
<h3 id="targetadd_imports">target:add_imports</h3>
<h4 id="">为自定义脚本预先导入扩展模块</h4>
<p>通常，我们在<a href="#targeton_build">on_build</a>等自定义脚本内部，可以通过<code>import("core.base.task")</code>的方式导入扩展模块，<br>但是对于自定义脚本比较多的情况下，每个自定义脚本都重复导入一遍，非常的繁琐，那么可以通过这个接口，实现预先导入，例如：</p>
<pre><code class="lang-lua">target("test")
    on_load(function (target)
        import("core.base.task")
        import("core.project.project")

        task.run("xxxx")
    end)
    on_build(function (target)
        import("core.base.task")
        import("core.project.project")

        task.run("xxxx")
    end)
    on_install(function (target)
        import("core.base.task")
        import("core.project.project")

        task.run("xxxx")
    end)
</code></pre>
<p>通过此接口可以简化为：</p>
<pre><code class="lang-lua">target("test")
    add_imports("core.base.task", "core.project.project")
    on_load(function (target)
        task.run("xxxx")
    end)
    on_build(function (target)
        task.run("xxxx")
    end)
    on_install(function (target)
        task.run("xxxx")
    end)
</code></pre>
<h3 id="targetadd_rules">target:add_rules</h3>
<h4 id="">添加规则到目标</h4>
<p>我们可以通过预先设置规则支持的文件后缀，来扩展其他文件的构建支持：</p>
<pre><code class="lang-lua">-- 定义一个markdown文件的构建规则
rule("markdown")
    set_extensions(".md", ".markdown")
    on_build(function (target, sourcefile)
        os.cp(sourcefile, path.join(target:targetdir(), path.basename(sourcefile) .. ".html"))
    end)

target("test")
    set_kind("binary")

    -- 使test目标支持markdown文件的构建规则
    add_rules("markdown")

    -- 添加markdown文件的构建
    add_files("src/*.md")
    add_files("src/*.markdown")
</code></pre>
<p>我们可以在add_rules时传参：</p>
<pre><code class="lang-lua">rule("my_rule")
    on_load(function (target)
        local my_arg = target:extraconf("rules", "my_rule", "my_arg") -- "my arg"
    end)

target("test")
    add_rules("my_rule", { my_arg = "my arg"})
</code></pre>
<p>我们也可以指定应用局部文件到规则，具体使用见：<a href="#targetadd_files">add_files</a>。</p>
<h3 id="targeton_load">target:on_load</h3>
<h4 id="">自定义目标加载脚本</h4>
<p>在target初始化加载的时候，将会执行此脚本，在里面可以做一些动态的目标配置，实现更灵活的目标描述定义，例如：</p>
<pre><code class="lang-lua">target("test")
    on_load(function (target)
        target:add("defines", "DEBUG", "TEST=\"hello\"")
        target:add("linkdirs", "/usr/lib", "/usr/local/lib")
        target:add({includedirs = "/usr/include", "links" = "pthread"})
    end)
</code></pre>
<p>可以在<code>on_load</code>里面，通过<code>target:set</code>, <code>target:add</code> 来动态添加各种target属性。</p>
<h3 id="targeton_config">target:on_config</h3>
<h4 id="">自定义配置脚本</h4>
<p>在 <code>xmake config</code> 执行完成后，Build 之前会执行此脚本，通常用于编译前的配置工作。它与 on_load 不同的是，on_load 只要 target 被加载就会执行，执行时机更早。</p>
<p>如果一些配置，无法在 on_load 中过早配置，那么都可以在 on_config 中去配置它。</p>
<p>另外，它的执行时机比 before_build 还要早，大概的执行流程如下：</p>
<pre><code>on_load -> after_load -> on_config -> before_build -> on_build -> after_build
</code></pre><h3 id="targeton_link">target:on_link</h3>
<h4 id="">自定义链接脚本</h4>
<p>这个是在v2.2.7之后新加的接口，用于定制化处理target的链接过程。</p>
<pre><code class="lang-lua">target("test")
    on_link(function (target)
        print("link it")
    end)
</code></pre>
<h3 id="targeton_build">target:on_build</h3>
<h4 id="">自定义编译脚本</h4>
<p>覆盖target目标默认的构建行为，实现自定义的编译过程，一般情况下，并不需要这么做，除非确实需要做一些xmake默认没有提供的编译操作。</p>
<p>你可以通过下面的方式覆盖它，来自定义编译操作：</p>
<pre><code class="lang-lua">target("test")

    -- 设置自定义编译脚本
    on_build(function (target)
        print("build it")
    end)
</code></pre>
<p>注：2.1.5版本之后，所有target的自定义脚本都可以针对不同平台和架构，分别处理，例如：</p>
<pre><code class="lang-lua">target("test")
    on_build("iphoneos|arm*", function (target)
        print("build for iphoneos and arm")
    end)
</code></pre>
<p>其中如果第一个参数为字符串，那么就是指定这个脚本需要在哪个<code>平台|架构</code>下，才会被执行，并且支持模式匹配，例如<code>arm*</code>匹配所有arm架构。</p>
<p>当然也可以只设置平台，不设置架构，这样就是匹配指定平台下，执行脚本：</p>
<pre><code class="lang-lua">target("test")
    on_build("windows", function (target)
        print("build for windows")
    end)
</code></pre>
<p>!> 一旦对这个target目标设置了自己的build过程，那么xmake默认的构建过程将不再被执行。</p>
<h3 id="targeton_build_file">target:on_build_file</h3>
<h4 id="">自定义编译脚本, 实现单文件构建</h4>
<p>通过此接口，可以用来hook指定target内置的构建过程，替换每个源文件编译过程：</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("src/*.c")
    on_build_file(function (target, sourcefile, opt)
    end)
</code></pre>
<p>如果不想重写内置的编译脚本，仅仅只是在编译前后添加一些自己的处理，其实用：<a href="#targetbefore_build_file">target.before_build_file</a>和<a href="#targetafter_build_file">target.after_build_file</a>会更加方便，不需要调用<code>opt.origin</code>。</p>
<h3 id="targeton_build_files">target:on_build_files</h3>
<h4 id="">自定义编译脚本, 实现多文件构建</h4>
<p>通过此接口，可以用来hook指定target内置的构建过程，替换一批同类型源文件编译过程：</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("src/*.c")
    on_build_files(function (target, sourcebatch, opt)
    end)
</code></pre>
<p>设置此接口后，对应源文件列表中文件，就不会出现在自定义的<a href="#targeton_build_file">target.on_build_file</a>了，因为这个是包含关系。</p>
<p>其中sourcebatch描述了这批同类型源文件：</p>
<ul>
<li><code>sourcebatch.sourcekind</code>: 获取这批源文件的类型，比如：cc, as, ..</li>
<li><code>sourcebatch.sourcefiles()</code>: 获取源文件列表</li>
<li><code>sourcebatch.objectfiles()</code>: 获取对象文件列表</li>
<li><code>sourcebatch.dependfiles()</code>: 获取对应依赖文件列表，存有源文件中编译依赖信息，例如：xxx.d</li>
</ul>
<h3 id="targeton_clean">target:on_clean</h3>
<h4 id="">自定义清理脚本</h4>
<p>覆盖target目标的<code>xmake [c|clean}</code>的清理操作，实现自定义清理过程。</p>
<pre><code class="lang-lua">target("test")

    -- 设置自定义清理脚本
    on_clean(function (target)

        -- 仅删掉目标文件
        os.rm(target:targetfile())
    end)
</code></pre>
<p>一些target接口描述如下：</p>
<table>
<thead>
<tr>
<th>target接口</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>target:name()</td>
<td>获取目标名</td>
</tr>
<tr>
<td>target:targetfile()</td>
<td>获取目标文件路径</td>
</tr>
<tr>
<td>target:get("kind")</td>
<td>获取目标的构建类型</td>
</tr>
<tr>
<td>target:get("defines")</td>
<td>获取目标的宏定义</td>
</tr>
<tr>
<td>target:get("xxx")</td>
<td>其他通过 <code>set_/add_</code>接口设置的target信息，都可以通过此接口来获取</td>
</tr>
<tr>
<td>target:add("links", "pthread")</td>
<td>添加目标设置</td>
</tr>
<tr>
<td>target:set("links", "pthread", "z")</td>
<td>覆写目标设置</td>
</tr>
<tr>
<td>target:deps()</td>
<td>获取目标的所有依赖目标</td>
</tr>
<tr>
<td>target:dep("depname")</td>
<td>获取指定的依赖目标</td>
</tr>
<tr>
<td>target:sourcebatches()</td>
<td>获取目标的所有源文件列表</td>
</tr>
</tbody>
</table>
<h3 id="targeton_package">target:on_package</h3>
<h4 id="">自定义打包脚本</h4>
<p>覆盖target目标的<code>xmake [p|package}</code>的打包操作，实现自定义打包过程，如果你想对指定target打包成自己想要的格式，可以通过这个接口自定义它。</p>
<p>这个接口还是挺实用的，例如，编译完jni后，将生成的so，打包进apk包中。</p>
<pre><code class="lang-lua">-- 定义一个android app的测试demo
target("demo")

    -- 生成动态库：libdemo.so
    set_kind("shared")

    -- 设置对象的输出目录，可选
    set_objectdir("$(buildir)/.objs")

    -- 每次编译完的libdemo.so的生成目录，设置为app/libs/armeabi
    set_targetdir("libs/armeabi")

    -- 添加jni的代码文件
    add_files("jni/*.c")

    -- 设置自定义打包脚本，在使用xmake编译完libdemo.so后，执行xmake p进行打包
    -- 会自动使用ant将app编译成apk文件
    --
    on_package(function (target)

        -- 使用ant编译app成apk文件，输出信息重定向到日志文件
        os.run("ant debug")
    end)
</code></pre>
<h3 id="targeton_install">target:on_install</h3>
<h4 id="">自定义安装脚本</h4>
<p>覆盖target目标的<code>xmake [i|install}</code>的安装操作，实现自定义安装过程。</p>
<p>例如，将生成的apk包，进行安装。</p>
<pre><code class="lang-lua">target("test")

    -- 设置自定义安装脚本，自动安装apk文件
    on_install(function (target)

        -- 使用adb安装打包生成的apk文件
        os.run("adb install -r ./bin/Demo-debug.apk")
    end)
</code></pre>
<h3 id="targeton_uninstall">target:on_uninstall</h3>
<h4 id="">自定义卸载脚本</h4>
<p>覆盖target目标的<code>xmake [u|uninstall}</code>的卸载操作，实现自定义卸载过程。</p>
<pre><code class="lang-lua">target("test")
    on_uninstall(function (target)
        ...
    end)
</code></pre>
<h3 id="targeton_run">target:on_run</h3>
<h4 id="">自定义运行脚本</h4>
<p>覆盖target目标的<code>xmake [r|run}</code>的运行操作，实现自定义运行过程。</p>
<p>例如，运行安装好的apk程序：</p>
<pre><code class="lang-lua">target("test")

    -- 设置自定义运行脚本，自动运行安装好的app程序，并且自动获取设备输出信息
    on_run(function (target)

        os.run("adb shell am start -n com.demo/com.demo.DemoTest")
        os.run("adb logcat")
    end)
</code></pre>
<h3 id="targetbefore_link">target:before_link</h3>
<h4 id="">在链接之前执行一些自定义脚本</h4>
<p>这个是在v2.2.7之后新加的接口，用于在链接之前增加一些自定义的操作。</p>
<pre><code class="lang-lua">target("test")
    before_link(function (target)
        print("")
    end)
</code></pre>
<h3 id="targetbefore_build">target:before_build</h3>
<h4 id="">在构建之前执行一些自定义脚本</h4>
<p>并不会覆盖默认的构建操作，只是在构建之前增加一些自定义的操作。</p>
<pre><code class="lang-lua">target("test")
    before_build(function (target)
        print("")
    end)
</code></pre>
<h3 id="targetbefore_build_file">target:before_build_file</h3>
<h4 id="">自定义编译前的脚本, 实现单文件构建</h4>
<p>通过此接口，可以用来hook指定target内置的构建过程，在每个源文件编译过程之前执行一些自定义脚本：</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("src/*.c")
    before_build_file(function (target, sourcefile, opt)
    end)
</code></pre>
<h3 id="targetbefore_build_files">target:before_build_files</h3>
<h4 id="">自定义编译前的脚本, 实现多文件构建</h4>
<p>通过此接口，可以用来hook指定target内置的构建过程，在一批同类型源文件编译过程之前执行一些自定义脚本：</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("src/*.c")
    before_build_files(function (target, sourcebatch, opt)
    end)
</code></pre>
<h3 id="targetbefore_clean">target:before_clean</h3>
<h4 id="">在清理之前执行一些自定义脚本</h4>
<p>并不会覆盖默认的清理操作，只是在清理之前增加一些自定义的操作。</p>
<pre><code class="lang-lua">target("test")
    before_clean(function (target)
        print("")
    end)
</code></pre>
<h3 id="targetbefore_package">target:before_package</h3>
<h4 id="">在打包之前执行一些自定义脚本</h4>
<p>并不会覆盖默认的打包操作，只是在打包之前增加一些自定义的操作。</p>
<pre><code class="lang-lua">target("test")
    before_package(function (target)
        print("")
    end)
</code></pre>
<h3 id="targetbefore_install">target:before_install</h3>
<h4 id="">在安装之前执行一些自定义脚本</h4>
<p>并不会覆盖默认的安装操作，只是在安装之前增加一些自定义的操作。</p>
<pre><code class="lang-lua">target("test")
    before_install(function (target)
        print("")
    end)
</code></pre>
<h3 id="targetbefore_uninstall">target:before_uninstall</h3>
<h4 id="">在卸载之前执行一些自定义脚本</h4>
<p>并不会覆盖默认的卸载操作，只是在卸载之前增加一些自定义的操作。</p>
<pre><code class="lang-lua">target("test")
    before_uninstall(function (target)
        print("")
    end)
</code></pre>
<h3 id="targetbefore_run">target:before_run</h3>
<h4 id="">在运行之前执行一些自定义脚本</h4>
<p>并不会覆盖默认的运行操作，只是在运行之前增加一些自定义的操作。</p>
<pre><code class="lang-lua">target("test")
    before_run(function (target)
        print("")
    end)
</code></pre>
<h3 id="targetafter_link">target:after_link</h3>
<h4 id="">在链接之后执行一些自定义脚本</h4>
<p>这个是在v2.2.7之后新加的接口，用于在链接之后增加一些自定义的操作。</p>
<pre><code class="lang-lua">target("test")
    after_link(function (target)
        print("")
    end)
</code></pre>
<h3 id="targetafter_build">target:after_build</h3>
<h4 id="">在构建之后执行一些自定义脚本</h4>
<p>并不会覆盖默认的构建操作，只是在构建之后增加一些自定义的操作。</p>
<p>例如，对于ios的越狱开发，构建完程序后，需要用<code>ldid</code>进行签名操作</p>
<pre><code class="lang-lua">target("test")
    after_build(function (target)
        os.run("ldid -S %s", target:targetfile())
    end)
</code></pre>
<h3 id="targetafter_build_file">target:after_build_file</h3>
<h4 id="">自定义编译前的脚本, 实现单文件构建</h4>
<p>通过此接口，可以用来hook指定target内置的构建过程，在每个源文件编译过程之后执行一些自定义脚本：</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("src/*.c")
    after_build_file(function (target, sourcefile, opt)
    end)
</code></pre>
<h3 id="targetafter_build_files">target:after_build_files</h3>
<h4 id="">自定义编译前的脚本, 实现多文件构建</h4>
<p>通过此接口，可以用来hook指定target内置的构建过程，在一批同类型源文件编译过程之后执行一些自定义脚本：</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("src/*.c")
    after_build_files(function (target, sourcebatch, opt)
    end)
</code></pre>
<h3 id="targetafter_clean">target:after_clean</h3>
<h4 id="">在清理之后执行一些自定义脚本</h4>
<p>并不会覆盖默认的清理操作，只是在清理之后增加一些自定义的操作。</p>
<p>一般可用于清理编译某target自动生成的一些额外的临时文件，这些文件xmake默认的清理规则可能没有清理到，例如：</p>
<pre><code class="lang-lua">target("test")
    after_clean(function (target)
        os.rm("$(buildir)/otherfiles")
    end)
</code></pre>
<h3 id="targetafter_package">target:after_package</h3>
<h4 id="">在打包之后执行一些自定义脚本</h4>
<p>并不会覆盖默认的打包操作，只是在打包之后增加一些自定义的操作。</p>
<pre><code class="lang-lua">target("test")
    after_package(function (target)
        print("")
    end)
</code></pre>
<h3 id="targetafter_install">target:after_install</h3>
<h4 id="">在安装之后执行一些自定义脚本</h4>
<p>并不会覆盖默认的安装操作，只是在安装之后增加一些自定义的操作。</p>
<pre><code class="lang-lua">target("test")
    after_install(function (target)
        print("")
    end)
</code></pre>
<h3 id="targetafter_uninstall">target:after_uninstall</h3>
<h4 id="">在卸载之后执行一些自定义脚本</h4>
<p>并不会覆盖默认的卸载操作，只是在卸载之后增加一些自定义的操作。</p>
<pre><code class="lang-lua">target("test")
    after_uninstall(function (target)
        print("")
    end)
</code></pre>
<h3 id="targetafter_run">target:after_run</h3>
<h4 id="">在运行之后执行一些自定义脚本</h4>
<p>并不会覆盖默认的运行操作，只是在运行之后增加一些自定义的操作。</p>
<pre><code class="lang-lua">target("test")
    after_run(function (target)
        print("")
    end)
</code></pre>
<h3 id="targetset_pcheader">target:set_pcheader</h3>
<h4 id="c">设置 C 预编译头文件</h4>
<p>xmake支持通过预编译头文件去加速c程序编译，目前支持的编译器有：gcc, clang和msvc。</p>
<p>使用方式如下：</p>
<pre><code class="lang-lua">target("test")
    set_pcheader("header.h")
</code></pre>
<h3 id="targetset_pcxxheader">target:set_pcxxheader</h3>
<h4 id="c">设置 C++ 预编译头文件</h4>
<p>xmake支持通过预编译头文件去加速c++程序编译，目前支持的编译器有：gcc, clang和msvc。</p>
<p>使用方式如下：</p>
<pre><code class="lang-lua">target("test")
    set_pcxxheader("header.h")
</code></pre>
<h3 id="targetset_pmheader">target:set_pmheader</h3>
<h4 id="objc">设置 ObjC 预编译头文件</h4>
<p>xmake支持通过预编译头文件去加速 ObjC 程序编译，目前支持的编译器有：gcc, clang和msvc。</p>
<p>使用方式如下：</p>
<pre><code class="lang-lua">target("test")
    set_pmheader("header.h")
</code></pre>
<h3 id="targetset_pmxxheader">target:set_pmxxheader</h3>
<h4 id="objc">设置 ObjC++ 预编译头文件</h4>
<p>xmake支持通过预编译头文件去加速 ObjC++ 程序编译，目前支持的编译器有：gcc, clang和msvc。</p>
<p>使用方式如下：</p>
<pre><code class="lang-lua">target("test")
    set_pmxxheader("header.h")
</code></pre>
<h3 id="targetadd_deps">target:add_deps</h3>
<h4 id="">添加子工程目标依赖</h4>
<p>添加当前目标的依赖目标，编译的时候，会去优先编译依赖的目标，然后再编译当前目标。。。</p>
<pre><code class="lang-lua">target("test1")
    set_kind("static")
    set_files("*.c")

target("test2")
    set_kind("static")
    set_files("*.c")

target("demo")
    add_deps("test1", "test2")
</code></pre>
<p>上面的例子，在编译目标demo的时候，需要先编译test1, test2目标，因为demo会去用到他们</p>
<p>!> target会自动继承依赖目标中的配置和属性，不需要额外调用<code>add_links</code>, <code>add_linkdirs</code>和<code>add_rpathdirs</code>等接口去关联依赖目标了。</p>
<p>并且继承关系是支持级联的，例如：</p>
<pre><code class="lang-lua">target("library1")
    set_kind("static")
    add_files("*.c")
    add_includedirs("inc") -- 默认私有头文件目录不会被继承
    add_includedirs("inc1", {public = true}) -- 此处的头文件相关目录也会被继承

target("library2")
    set_kind("static")
    add_deps("library1")
    add_files("*.c")

target("test")
    set_kind("binary")
    add_deps("library2")
</code></pre>
<p>如果我们不想继承依赖target的任何配置，如何操作呢？</p>
<pre><code class="lang-lua">add_deps("dep1", "dep2", {inherit = false})
</code></pre>
<p>通过显式设置inherit配置，来告诉xmake，这两个依赖的配置是否需要被继承，如果不设置，默认就是启用继承的。</p>
<p>2.2.5版本之后，可通过 <code>add_includedirs("inc1", {public = true})</code>, 设置public为true, 将includedirs的设置公开给其他依赖的子target继承。</p>
<p>目前对于target的编译链接flags相关接口设置，都是支持继承属性的，可以人为控制是否需要导出给其他target来依赖继承，目前支持的属性有：</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>private</td>
<td>默认设置，作为当前target的私有配置，不会被依赖的其他target所继承</td>
</tr>
<tr>
<td>public</td>
<td>公有配置，当前target，依赖的子target都会被设置</td>
</tr>
<tr>
<td>interface</td>
<td>接口设置，仅被依赖的子target所继承设置，当前target不参与</td>
</tr>
</tbody>
</table>
<p>对于这块的详细说明，可以看下：<a href="https://github.com/xmake-io/xmake/issues/368">https://github.com/xmake-io/xmake/issues/368</a></p>
<h3 id="targetadd_links">target:add_links</h3>
<h4 id="">添加链接库名</h4>
<p>为当前目标添加链接库，一般这个要与<a href="#targetadd_linkdirs">add_linkdirs</a>配对使用。</p>
<pre><code class="lang-lua">target("demo")

    -- 添加对libtest.a的链接，相当于 -ltest
    add_links("test")

    -- 添加链接搜索目录
    add_linkdirs("$(buildir)/lib")
</code></pre>
<p>2.8.1 版本开始，add_links 还支持添加库的完整路径，例如：<code>add_links("/tmp/libfoo.a")</code>，显式的指定库文件。</p>
<h3 id="targetadd_syslinks">target:add_syslinks</h3>
<h4 id="">添加系统链接库名</h4>
<p>这个接口使用上跟<a href="#targetadd_links">add_links</a>类似，唯一的区别就是，通过这个接口添加的链接库顺序在所有<code>add_links</code>之后。</p>
<p>因此主要用于添加系统库依赖，因为系统库的链接顺序是非常靠后的，例如：</p>
<pre><code class="lang-lua">add_syslinks("pthread", "m", "dl")
target("demo")
    add_links("a", "b")
    add_linkdirs("$(buildir)/lib")
</code></pre>
<p>上面的配置，即使<code>add_syslinks</code>被优先提前设置了，但最后的链接顺序依然是：<code>-la -lb -lpthread -lm -ldl</code></p>
<h3 id="targetadd_linkorders">target:add_linkorders</h3>
<h4 id="">调整链接顺序</h4>
<p>这是 xmake 2.8.5 以后得版本才支持的特性，主要用于调整 target 内部的链接顺序。</p>
<p>由于 xmake 提供了 <code>add_links</code>, <code>add_deps</code>, <code>add_packages</code>, <code>add_options</code> 接口，可以配置目标、依赖，包和选项中的链接。</p>
<p>但是它们之间的链接顺序，在之前可控性比较弱，只能按固定顺序生成，这对于一些复杂的项目，就有点显得力不从心了。</p>
<p>更多详情和背景见：<a href="https://github.com/xmake-io/xmake/issues/1452">#1452</a></p>
<h5 id="">排序链接</h5>
<p>为了更加灵活的调整 target 内部的各种链接顺序，我们新增了 <code>add_linkorders</code> 接口，用于配置目标、依赖、包、选项、链接组引入的各种链接顺序。</p>
<p>例如：</p>
<pre><code class="lang-lua">add_links("a", "b", "c", "d", "e")
-- e -> b -> a
add_linkorders("e", "b", "a")
-- e -> d
add_linkorders("e", "d")
</code></pre>
<p>add_links 是配置的初始链接顺序，然后我们通过 add_linkorders 配置了两个局部链接依赖 <code>e -> b -> a</code> 和 <code>e -> d</code> 后。</p>
<p>xmake 内部就会根据这些配置，生成 DAG 图，通过拓扑排序的方式，生成最终的链接顺序，提供给链接器。</p>
<p>当然，如果存在循环依赖，产生了环，它也会提供警告信息。</p>
<h5 id="">排序链接和链接组</h5>
<p>另外，对于循环依赖，我们也可以通过 <code>add_linkgroups</code> 配置链接组的方式也解决。</p>
<p>并且 <code>add_linkorders</code> 也能够对链接组进行排序。</p>
<pre><code class="lang-lua">add_links("a", "b", "c", "d", "e")
add_linkgroups("c", "d", {name = "foo", group = true})
add_linkorders("e", "linkgroup::foo")
</code></pre>
<p>如果要排序链接组，我们需要对每个链接组取个名，<code>{name = "foo"}</code> ，然后就能在 <code>add_linkorders</code> 里面通过 <code>linkgroup::foo</code> 去引用配置了。</p>
<p>2.9.6 版本新增 as_needed 配置项，可以用于禁用 as_needed。（默认不配置，就是开启状态。）</p>
<pre><code class="lang-lua">add_linkgroups("c", "d", {as_needed = false})
</code></pre>
<p>对应的 flags 如下。</p>
<pre><code class="lang-bash">-Wl,--no-as-needed c d -Wl,--as-needed
</code></pre>
<h5 id="frameworks">排序链接和frameworks</h5>
<p>我们也可以排序链接和 macOS/iPhoneOS 的 frameworks。</p>
<pre><code class="lang-lua">add_links("a", "b", "c", "d", "e")
add_frameworks("Foundation", "CoreFoundation")
add_linkorders("e", "framework::CoreFoundation")
</code></pre>
<h5 id="">完整例子</h5>
<p>相关的完整例子，我们可以看下：</p>
<pre><code class="lang-lua">add_rules("mode.debug", "mode.release")

add_requires("libpng")

target("bar")
    set_kind("shared")
    add_files("src/foo.cpp")
    add_linkgroups("m", "pthread", {whole = true})

target("foo")
    set_kind("static")
    add_files("src/foo.cpp")
    add_packages("libpng", {public = true})

target("demo")
    set_kind("binary")
    add_deps("foo")
    add_files("src/main.cpp")
    if is_plat("linux", "macosx") then
        add_syslinks("pthread", "m", "dl")
    end
    if is_plat("macosx") then
        add_frameworks("Foundation", "CoreFoundation")
    end
    add_linkorders("framework::Foundation", "png16", "foo")
    add_linkorders("dl", "linkgroup::syslib")
    add_linkgroups("m", "pthread", {name = "syslib", group = true})
</code></pre>
<p>完整工程在：<a href="https://github.com/xmake-io/xmake/blob/master/tests/projects/c%2B%2B/linkorders/xmake.lua">linkorders example</a></p>
<h3 id="targetadd_linkgroups">target:add_linkgroups</h3>
<h4 id="">添加链接组</h4>
<p>这是 xmake 2.8.5 以后得版本才支持的特性，这个链接组的特性，目前主要用于 linux 平台的编译，仅支持 gcc/clang 编译器。</p>
<p>需要注意的是 gcc/clang 里面的链接组概念主要特指：<code>-Wl,--start-group</code></p>
<p>而 xmake 对齐进行了封装，做了进一步抽象，并且不仅仅用于处理 <code>-Wl,--start-group</code>，还可以处理 <code>-Wl,--whole-archive</code> 和 <code>-Wl,-Bstatic</code>。</p>
<p>下面我们会一一对其进行讲解。</p>
<p>更多详情见：<a href="https://github.com/xmake-io/xmake/issues/1452">#1452</a></p>
<h5 id="startgroup">--start-group 支持</h5>
<p><code>-Wl,--start-group</code> 和 <code>-Wl,--end-group</code> 是用于处理复杂库依赖关系的链接器选项，确保链接器可以解决符号依赖并成功连接多个库。</p>
<p>在 xmake 中，我们可以通过下面的方式实现：</p>
<pre><code class="lang-lua">add_linkgroups("a", "b", {group = true})
</code></pre>
<p>它会对应生成 <code>-Wl,--start-group -la -lb -Wl,--end-group</code> 链接选项。</p>
<p>如果 a 和 b 库之间有符号的循环依赖，也不会报链接错误，能够正常链接成功。</p>
<p>对于不支持的平台和编译，会退化成 <code>-la -lb</code></p>
<h5 id="wholearchive">--whole-archive 支持</h5>
<p><code>--whole-archive</code> 是一个链接器选项，通常用于处理静态库。<br>它的作用是告诉链接器将指定的静态库中的所有目标文件都包含到最终可执行文件中，而不仅仅是满足当前符号依赖的目标文件。<br>这可以用于确保某些库的所有代码都被链接，即使它们在当前的符号依赖关系中没有直接引用。</p>
<p>更多信息，可以参考 gcc/clang 的文档。</p>
<p>在 xmake 中，我们可以通过下面的方式实现：</p>
<pre><code class="lang-lua">add_linkgroups("a", "b", {whole = true})
</code></pre>
<p>它会对应生成 <code>-Wl,--whole-archive -la -lb -Wl,--no-whole-archive</code> 链接选项。</p>
<p>对于不支持的平台和编译，会退化成 <code>-la -lb</code></p>
<p>另外，我们可以同时配置 group/whole：</p>
<pre><code class="lang-lua">add_linkgroups("a", "b", {whole = true, group = true})
</code></pre>
<h5 id="bstatic">-Bstatic 支持</h5>
<p><code>-Bstatic</code> 也是用于编译器（如gcc）的选项，用于指示编译器在链接时只使用静态库而不使用共享库。</p>
<p>更多信息，可以参考 gcc/clang 的文档。</p>
<p>在 xmake 中，我们可以通过下面的方式实现：</p>
<pre><code class="lang-lua">add_linkgroups("a", "b", {static = true})
</code></pre>
<p>它会对应生成 <code>-Wl,-Bstatic -la -lb -Wl,-Bdynamic</code> 链接选项。</p>
<h3 id="targetadd_files">target:add_files</h3>
<h4 id="">添加源代码文件</h4>
<p>用于添加目标工程的源文件，甚至库文件，目前支持的一些文件类型：</p>
<table>
<thead>
<tr>
<th>支持的源文件类型</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>.c/.cpp/.cc/.cxx</td>
<td>c++文件</td>
</tr>
<tr>
<td>.s/.S/.asm</td>
<td>汇编文件</td>
</tr>
<tr>
<td>.m/.mm</td>
<td>objc文件</td>
</tr>
<tr>
<td>.swift</td>
<td>swift文件</td>
</tr>
<tr>
<td>.go</td>
<td>golang文件</td>
</tr>
<tr>
<td>.o/.obj</td>
<td>对象文件</td>
</tr>
<tr>
<td>.a/.lib</td>
<td>静态库文件，会自动合并库到目标程序</td>
</tr>
<tr>
<td>.rc</td>
<td>msvc的资源文件</td>
</tr>
<tr>
<td>.manifest</td>
<td>windows manifest 文件</td>
</tr>
<tr>
<td>.def</td>
<td>windows dll 导出文件</td>
</tr>
<tr>
<td>.ld/.lds</td>
<td>linker scripts 文件，通常用于 gcc/clang</td>
</tr>
<tr>
<td>.map/.ver</td>
<td>version script 文件，通常用于 gcc/clang</td>
</tr>
</tbody>
</table>
<p>其中通配符<code>*</code>表示匹配当前目录下文件，而<code>**</code>则匹配多级目录下的文件。</p>
<p>例如：</p>
<pre><code class="lang-lua">add_files("src/test_*.c")
add_files("src/xxx/**.cpp")
add_files("src/asm/*.S", "src/objc/**/hello.m")
</code></pre>
<p><code>add_files</code>的使用其实是相当灵活方便的，其匹配模式借鉴了premake的风格，但是又对其进行了改善和增强。</p>
<p>使得不仅可以匹配文件，还有可以在添加文件同时，过滤排除指定模式的一批文件。</p>
<p>例如：</p>
<pre><code class="lang-lua">-- 递归添加src下的所有c文件，但是不包括src/impl/下的所有c文件
add_files("src/**.c|impl/*.c")

-- 添加src下的所有cpp文件，但是不包括src/test.cpp、src/hello.cpp以及src下所有带xx_前缀的cpp文件
add_files("src/*.cpp|test.cpp|hello.cpp|xx_*.cpp")
</code></pre>
<p>其中分隔符<code>|</code>之后的都是需要排除的文件，这些文件也同样支持匹配模式，并且可以同时添加多个过滤模式，只要中间用<code>|</code>分割就行了。。</p>
<p>添加文件的时候支持过滤一些文件的一个好处就是，可以为后续根据不同开关逻辑添加文件提供基础。</p>
<p><p class="tip"><br>为了使得描述上更加的精简，<code>|</code>之后的过滤描述都是基于起一个模式：<code>src/*.cpp</code> 中<code>*</code>之前的目录为基础的。<br>所以上面的例子后面过滤的都是在src下的文件，这个是要注意的。<br></p>

</p>
<p>2.1.6版本之后，对<code>add_files</code>进行了改进，支持基于files更细粒度的编译选项控制，例如：</p>
<pre><code class="lang-lua">target("test")
    add_defines("TEST1")
    add_files("src/*.c")
    add_files("test/*.c", "test2/test2.c", {defines = "TEST2", languages = "c99", includedirs = ".", cflags = "-O0"})
</code></pre>
<p>可以在<code>add_files</code>的最后一个参数，传入一个配置table，去控制指定files的编译选项，里面的配置参数跟target的一致，并且这些文件还会继承target的通用配置<code>-DTEST1</code>。</p>
<p>2.1.9版本之后，支持添加未知的代码文件，通过设置rule自定义规则，实现这些文件的自定义构建，例如：</p>
<pre><code class="lang-lua">target("test")
    -- ...
    add_files("src/test/*.md", {rule = "markdown"})
</code></pre>
<p>关于自定义构建规则的使用说明，详细见：<a href="#构建规则">构建规则</a>。</p>
<p>并且在2.1.9版本之后，可以通过force参数来强制禁用cxflags,cflags等编译选项的自动检测，直接传入编译器，哪怕编译器有可能不支持，也会设置：</p>
<pre><code class="lang-lua">add_files("src/*.c", {force = {cxflags = "-DTEST", mflags = "-framework xxx"}})
</code></pre>
<h3 id="targetremove_files">target:remove_files</h3>
<h4 id="">从前面的源代码文件列表中删除指定文件</h4>
<p>通过此接口，可以从前面<a href="targetadd_files">add_files</a>接口添加的文件列表中，删除指定的文件，例如：</p>
<pre><code class="lang-lua">target("test")
    add_files("src/*.c")
    remove_files("src/test.c")
</code></pre>
<p>上面的例子，可以从<code>src</code>目录下添加除<code>test.c</code>以外的所有文件，当然这个也可以通过<code>add_files("src/*.c|test.c")</code>来达到相同的目的，但是这种方式更加灵活。</p>
<p>例如，我们可以条件判断来控制删除哪些文件，并且此接口也支持<a href="targetadd_files">add_files</a>的匹配模式，过滤模式，进行批量移除。</p>
<pre><code class="lang-lua">target("test")
    add_files("src/**.c")
    remove_files("src/test*.c")
    remove_files("src/subdir/*.c|xxx.c")
    if is_plat("iphoneos") then
        add_files("xxx.m")
    end
</code></pre>
<p>通过上面的例子，我们可以看出<code>add_files</code>和<code>remove_files</code>是根据调用顺序，进行顺序添加和删除的，并且通过<code>remove_files("src/subdir/*.c|xxx.c")</code>删除一批文件，<br>并且排除<code>src/subdir/xxx.c</code>（就是说，不删除这个文件）。</p>
<p>注： 这个接口 v2.6.3 版本才提供，之前的版本是 del_files，已经废弃。</p>
<p>如果向下要兼容以前的版本，可以通过下面的配置解决。</p>
<pre><code class="lang-lua">remove_files = remove_files or del_files
</code></pre>
<h3 id="targetremove_headerfiles">target:remove_headerfiles</h3>
<h4 id="">从前面的头文件列表中删除指定文件</h4>
<p>主要用于从 <code>add_headerfiles</code> 设置的头文件列表中删除文件，用法与 <code>remove_files</code> 类似。</p>
<p>这个接口，v2.6.3 版本才提供。</p>
<h3 id="targetadd_linkdirs">target:add_linkdirs</h3>
<h4 id="">添加链接库搜索目录</h4>
<p>设置链接库的搜索目录，这个接口的使用方式如下：</p>
<pre><code class="lang-lua">target("test")
    add_linkdirs("$(buildir)/lib")
</code></pre>
<p>此接口相当于gcc的<code>-Lxxx</code>链接选项。</p>
<p>一般他是与<a href="#targetadd_links">add_links</a>配合使用的，当然也可以直接通过<a href="#targetadd_ldflags">add_ldflags</a>或者<a href="#targetadd_shflags">add_shflags</a>接口来添加，也是可以的。</p>
<p><p class="tip"><br>如果不想在工程中写死，可以通过：<code>xmake f --linkdirs=xxx</code>或者<code>xmake f --ldflags="-L/xxx"</code>的方式来设置，当然这种手动设置的目录搜索优先级更高。<br></p>

</p>
<h3 id="targetadd_rpathdirs">target:add_rpathdirs</h3>
<h4 id="">添加程序运行时动态库的加载搜索目录</h4>
<p>通过<a href="#targetadd_linkdirs">add_linkdirs</a>设置动态库的链接搜索目录后，程序被正常链接，但是在linux平台想要正常运行编译后的程序，会报加载动态库失败。</p>
<p>因为没找到动态库的加载目录，想要正常运行依赖动态库的程序，需要设置<code>LD_LIBRARY_PATH</code>环境变量，指定需要加载的动态库目录。</p>
<p>但是这种方式是全局的，影响太广，更好的方式是通过<code>-rpath=xxx</code>的链接器选项，在链接程序的时候设置好需要加载的动态库搜索路径，而xmake对其进行了封装，通过<code>add_rpathdirs</code>更好的处理跨平台问题。</p>
<p>具体使用如下：</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_linkdirs("$(buildir)/lib")
    add_rpathdirs("$(buildir)/lib")
</code></pre>
<p>只需要在链接的时候，在设置下rpath目录就好了，虽然也可以通过<code>add_ldflags("-Wl,-rpath=xxx")</code>达到相同的目的，但是这个接口更加通用。</p>
<p>内部会对不同平台进行处理，像在macOS下，是不需要<code>-rpath</code>设置的，也是可以正常加载运行程序，因此针对这个平台，xmake内部会直接忽略器设置，避免链接报错。</p>
<p>而在为dlang程序进行动态库链接时，xmake会自动处理成<code>-L-rpath=xxx</code>来传入dlang的链接器，这样就避免了直接使用<code>add_ldflags</code>需要自己判断和处理不同平台和编译器问题。</p>
<p>2.1.7版本对这个接口进行了改进，支持：<code>@loader_path</code>, <code>@executable_path</code> 和 <code>$ORIGIN</code>的内置变量，来指定程序的加载目录，它们的效果基本上是一样的，主要是为了同时兼容macho, elf。</p>
<p>例如：</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_linkdirs("$(buildir)/lib")
    add_rpathdirs("@loader_path/lib")
</code></pre>
<p>指定test程序加载当前执行目录下<code>lib/*.[so|dylib]</code>的动态库文件，这将有助于提升程序的可移植性，不用写死绝对路径和相对路径，导致程序和目录切换引起程序加载动态库失败。</p>
<p>!> 需要注意的是，在macos下，要想 add_rpathdirs 设置生效，需要对dylib做一些预处理，添加<code>@rpath/xxx</code>路径设置：<br><code>$install_name_tool -add_rpath @rpath/libxxx.dylib xxx/libxxx.dylib</code><br>我们也可以通过<code>otool -L libxxx.dylib</code>查看是否存在带@rpath的路径</p>
<p>另外，对于 gcc， <code>add_rpathdirs</code> 默认设置的是 runpath，如果想要显式的配置上 <code>-Wl,--enable-new-dtags</code>, <code>-Wl,--disable-new-dtags</code> 去配置 rpath 还是 runpath</p>
<p>我们可以通过额外的参数指定，<code>add_rpathdirs("xxx", {runpath = true})</code></p>
<p>相关背景细节见：<a href="https://github.com/xmake-io/xmake/issues/5109">#5109</a></p>
<p>2.9.4 之后，我们新增了 <code>add_rpathdirs("xxx", {install_only = true})</code> ，可以单独配置安装后的 rpath 路径。</p>
<h3 id="targetadd_includedirs">target:add_includedirs</h3>
<h4 id="">添加头文件搜索目录</h4>
<p>设置头文件的搜索目录，这个接口的使用方式如下：</p>
<pre><code class="lang-lua">target("test")
    add_includedirs("$(buildir)/include")
</code></pre>
<p>当然也可以直接通过<a href="#targetadd_cxflags">add_cxflags</a>或者<a href="#targetadd_mxflags">add_mxflags</a>等接口来设置，也是可以的。</p>
<p>2.2.5之后，可通过额外的<code>{public|interface = true}</code>属性设置，将includedirs导出给依赖的子target，例如：</p>
<pre><code class="lang-lua">target("test")
    set_kind("static")
    add_includedirs("src/include") -- 仅对当前target生效
    add_includedirs("$(buildir)/include", {public = true})，当前target和子target都会被设置

target("demo")
    set_kind("binary")
    add_deps("test")
</code></pre>
<p>更多关于这块的说明，见：<a href="#targetadd_deps">add_deps</a></p>
<p>!> 如果不想在工程中写死，可以通过：<code>xmake f --includedirs=xxx</code>或者<code>xmake f --cxflags="-I/xxx"</code>的方式来设置，当然这种手动设置的目录搜索优先级更高。</p>
<p>!> 头文件默认不支持模式匹配，也不推荐这么做， 容易引入一些不需要的子目录，导致各种头文件引用冲突干扰，出了问题更难查。<br>如果用户非要这么做，可以通过 <code>add_includedirs(os.dirs(path.join(os.scriptdir(), "xxx/**")))</code> 来实现。</p>
<h3 id="targetadd_sysincludedirs">target:add_sysincludedirs</h3>
<h4 id="">添加系统头文件搜索目录</h4>
<p><code>add_includedirs</code> 通常用于添加工程头文件搜索目录，而一些系统库头文件的引入，有可能会触发一些内部的警告信息，但是这些警告对于用户来讲也许是无法避免，也修复不了的。</p>
<p>那么，每次显示这些警告反而会干扰用户，因此，gcc/clang 提供了 <code>-isystem</code> 专门用来设置系统头文件搜索路径，通过此接口设置的头文件，会压制一些警告信息来避免干扰用户。</p>
<p>msvc 也通提供了 <code>/external:I</code> 编译选项来设置它，但是需要高版本 msvc 才支持。</p>
<p>因此，xmake 提供了 <code>add_sysincludedirs</code> 来抽象适配设置系统库头文件搜索路径，如果当前编译器不支持，会自动切换回 <code>-I</code> 编译选项。</p>
<pre><code class="lang-lua">target("test")
    add_sysincludedirs("/usr/include")
</code></pre>
<p>生成的编译选项如下：</p>
<pre><code class="lang-console">-isystem /usr/include
</code></pre>
<p>如果是 msvc 编译器，则会是：</p>
<pre><code class="lang-console">/experimental:external /external:W0 /external:I /usr/include
</code></pre>
<p>!> 另外，使用 <code>add_requires()</code> 引入的依赖包，默认也会使用 <code>-isystem</code> 作为外部系统头文件。</p>
<h3 id="targetadd_defines">target:add_defines</h3>
<h4 id="">添加宏定义</h4>
<pre><code class="lang-lua">add_defines("DEBUG", "TEST=0", "TEST2=\"hello\"")
</code></pre>
<p>相当于设置了编译选项：</p>
<pre><code>-DDEBUG -DTEST=0 -DTEST2=\"hello\"
</code></pre><h3 id="targetadd_undefines">target:add_undefines</h3>
<h4 id="">取消宏定义</h4>
<pre><code class="lang-lua">add_undefines("DEBUG")
</code></pre>
<p>相当于设置了编译选项：<code>-UDEBUG</code></p>
<p>在代码中相当于：<code>#undef DEBUG</code></p>
<h3 id="targetadd_cflags">target:add_cflags</h3>
<h4 id="c">添加c编译选项</h4>
<p>仅对c代码添加编译选项</p>
<pre><code class="lang-lua">add_cflags("-g", "-O2", "-DDEBUG")
</code></pre>
<p>!> 所有选项值都基于gcc的定义为标准，如果其他编译器不兼容（例如：vc），xmake会自动内部将其转换成对应编译器支持的选项值。<br>用户无需操心其兼容性，如果其他编译器没有对应的匹配值，那么xmake会自动忽略器设置。</p>
<p>在2.1.9版本之后，可以通过force参数来强制禁用flags的自动检测，直接传入编译器，哪怕编译器有可能不支持，也会设置：</p>
<pre><code class="lang-lua">add_cflags("-g", "-O2", {force = true})
</code></pre>
<h3 id="targetadd_cxflags">target:add_cxflags</h3>
<h4 id="cc">添加c/c++编译选项</h4>
<p>同时对c/c++代码添加编译选项，用法跟 add_cflags 一致。</p>
<h3 id="targetadd_cxxflags">target:add_cxxflags</h3>
<h4 id="c">添加c++编译选项</h4>
<p>仅对c++代码添加编译选项，用法跟 add_cflags 一致。</p>
<h5 id="flags">添加特定编译器 flags</h5>
<p>2.7.3 版本中，我们改进了所有 flags 添加接口，可以仅仅对特定编译器指定 flags，例如：</p>
<pre><code class="lang-lua">add_cxxflags("clang::-stdlib=libc++")
add_cxxflags("gcc::-stdlib=libc++")
add_cxxflags("cl::/GR-")
add_cxxflags("clang_cl::/GR-")
</code></pre>
<p>或者：</p>
<pre><code class="lang-lua">add_cxxflags("-stdlib=libc++", {tools = "clang"})
add_cxxflags("-stdlib=libc++", {tools = "gcc"})
add_cxxflags("/GR-", {tools = {"clang_cl", "cl"}})
</code></pre>
<p>!> 不仅仅是编译flags，对 add_ldflags 等链接 flags，也是同样生效的。</p>
<h3 id="targetadd_mflags">target:add_mflags</h3>
<h4 id="objc">添加objc编译选项</h4>
<p>仅对objc代码添加编译选项</p>
<pre><code class="lang-lua">add_mflags("-g", "-O2", "-DDEBUG")
</code></pre>
<p>在2.1.9版本之后，可以通过force参数来强制禁用flags的自动检测，直接传入编译器，哪怕编译器有可能不支持，也会设置：</p>
<pre><code class="lang-lua">add_mflags("-g", "-O2", {force = true})
</code></pre>
<h3 id="targetadd_mxflags">target:add_mxflags</h3>
<h4 id="objcobjc">添加objc/objc++编译选项</h4>
<p>同时对objc/objc++代码添加编译选项</p>
<pre><code class="lang-lua">add_mxflags("-framework CoreFoundation")
</code></pre>
<h3 id="targetadd_mxxflags">target:add_mxxflags</h3>
<h4 id="objc">添加objc++编译选项</h4>
<p>仅对objc++代码添加编译选项</p>
<pre><code class="lang-lua">add_mxxflags("-framework CoreFoundation")
</code></pre>
<h3 id="targetadd_scflags">target:add_scflags</h3>
<h4 id="swift">添加swift编译选项</h4>
<p>对swift代码添加编译选项</p>
<pre><code class="lang-lua">add_scflags("xxx")
</code></pre>
<h3 id="targetadd_asflags">target:add_asflags</h3>
<h4 id="">添加汇编编译选项</h4>
<p>对汇编代码添加编译选项</p>
<pre><code class="lang-lua">add_asflags("xxx")
</code></pre>
<h3 id="targetadd_gcflags">target:add_gcflags</h3>
<h4 id="go">添加go编译选项</h4>
<p>对golang代码添加编译选项</p>
<pre><code class="lang-lua">add_gcflags("xxx")
</code></pre>
<h3 id="targetadd_dcflags">target:add_dcflags</h3>
<h4 id="dlang">添加dlang编译选项</h4>
<p>对dlang代码添加编译选项</p>
<pre><code class="lang-lua">add_dcflags("xxx")
</code></pre>
<h3 id="targetadd_rcflags">target:add_rcflags</h3>
<h4 id="rust">添加rust编译选项</h4>
<p>对rust代码添加编译选项</p>
<pre><code class="lang-lua">add_rcflags("xxx")
</code></pre>
<h3 id="targetadd_fcflags">target:add_fcflags</h3>
<h4 id="fortran">添加fortran编译选项</h4>
<p>对fortran代码添加编译选项</p>
<pre><code class="lang-lua">add_fcflags("xxx")
</code></pre>
<h3 id="targetadd_zcflags">target:add_zcflags</h3>
<h4 id="zig">添加zig编译选项</h4>
<p>对zig代码添加编译选项</p>
<pre><code class="lang-lua">add_zcflags("xxx")
</code></pre>
<h3 id="targetadd_cuflags">target:add_cuflags</h3>
<h4 id="cuda">添加cuda编译选项</h4>
<p>对cuda代码添加编译选项</p>
<pre><code class="lang-lua">add_cuflags("-gencode arch=compute_30,code=sm_30")
</code></pre>
<h3 id="targetadd_culdflags">target:add_culdflags</h3>
<h4 id="cuda">添加cuda设备链接选项</h4>
<p>v2.2.7之后，cuda默认构建会使用device-link，这个阶段如果要设置一些链接flags，则可以通过这个接口来设置。<br>而最终的程序链接，会使用ldflags，不会调用nvcc，直接通过gcc/clang等c/c++链接器来链接。</p>
<p>关于device-link的说明，可以参考：<a href="https://devblogs.nvidia.com/separate-compilation-linking-cuda-device-code/">https://devblogs.nvidia.com/separate-compilation-linking-cuda-device-code/</a></p>
<pre><code class="lang-lua">add_culdflags("-gencode arch=compute_30,code=sm_30")
</code></pre>
<h3 id="targetadd_cugencodes">target:add_cugencodes</h3>
<h4 id="cudagencode">添加cuda设备的gencode设置</h4>
<p><code>add_cugencodes()</code>接口其实就是对<code>add_cuflags("-gencode arch=compute_xx,code=compute_xx")</code>编译flags设置的简化封装，其内部参数值对应的实际flags映射关系如下：</p>
<pre><code class="lang-lua">- compute_xx                   --> `-gencode arch=compute_xx,code=compute_xx`
- sm_xx                        --> `-gencode arch=compute_xx,code=sm_xx`
- sm_xx,sm_yy                  --> `-gencode arch=compute_xx,code=[sm_xx,sm_yy]`
- compute_xx,sm_yy             --> `-gencode arch=compute_xx,code=sm_yy`
- compute_xx,sm_yy,sm_zz       --> `-gencode arch=compute_xx,code=[sm_yy,sm_zz]`
- native                       --> match the fastest cuda device on current host,
                                   eg. for a Tesla P100, `-gencode arch=compute_60,code=sm_60` will be added,
                                   if no available device is found, no `-gencode` flags will be added
</code></pre>
<p>例如：</p>
<pre><code class="lang-lua">add_cugencodes("sm_30")
</code></pre>
<p>就等价为</p>
<pre><code class="lang-lua">add_cuflags("-gencode arch=compute_30,code=sm_30")
add_culdflags("-gencode arch=compute_30,code=sm_30")
</code></pre>
<p>是不是上面的更加精简些，这其实就是个用于简化设置的辅助接口。</p>
<p>而如果我们设置了native值，那么xmake会自动探测当前主机的cuda设备，然后快速匹配到它对应的gencode设置，自动追加到整个构建过程中。</p>
<p>例如，如果我们主机目前的GPU是Tesla P100，并且能够被xmake自动检测到，那么下面的设置：</p>
<pre><code class="lang-lua">add_cugencodes("native")
</code></pre>
<p>等价于：</p>
<pre><code class="lang-lua">add_cugencodes("sm_60")
</code></pre>
<h3 id="targetadd_ldflags">target:add_ldflags</h3>
<h4 id="">添加链接选项</h4>
<p>添加静态链接选项</p>
<pre><code class="lang-lua">add_ldflags("-L/xxx", "-lxxx")
</code></pre>
<p>在添加链接选项时，默认无法支持参数内有空格，使用expand = false：</p>
<pre><code class="lang-lua">-- add_ldflags("-L/my lib") ERROR: Invalid arguments
add_ldflags({"-L/my lib"}, {expand = false}) -- OK
</code></pre>
<h3 id="targetadd_arflags">target:add_arflags</h3>
<h4 id="">添加静态库归档选项</h4>
<p>影响对静态库的生成</p>
<pre><code class="lang-lua">add_arflags("xxx")
</code></pre>
<h3 id="targetadd_shflags">target:add_shflags</h3>
<h4 id="">添加动态库链接选项</h4>
<p>影响对动态库的生成</p>
<pre><code class="lang-lua">add_shflags("xxx")
</code></pre>
<h3 id="targetadd_options">target:add_options</h3>
<h4 id="">添加关联选项</h4>
<p>这个接口跟<a href="#targetset_options">set_options</a>类似，唯一的区别就是，此处是追加选项，而<a href="#targetset_options">set_options</a>每次设置会覆盖先前的设置。</p>
<h3 id="targetadd_packages">target:add_packages</h3>
<h4 id="">添加包依赖</h4>
<p>在target作用域中，添加集成包依赖，例如：</p>
<pre><code class="lang-lua">target("test")
    add_packages("zlib", "polarssl", "pcre", "mysql")
</code></pre>
<p>这样，在编译test目标时，如果这个包存在的，将会自动追加包里面的宏定义、头文件搜索路径、链接库目录，也会自动链接包中所有库。</p>
<p>用户不再需要自己单独调用<a href="#targetadd_links">add_links</a>，<a href="#targetadd_includedirs">add_includedirs</a>, <a href="#targetadd_ldflags">add_ldflags</a>等接口，来配置依赖库链接了。</p>
<p>对于如何设置包搜索目录，可参考：<a href="/mirror/zh-cn/manual/global_interfaces.html#add_packagedirs">add_packagedirs</a> 接口</p>
<p>而在v2.2.2版本之后，此接口也同时支持远程依赖包管理中<a href="/mirror/zh-cn/manual/global_interfaces.html#add_requires">add_requires</a>定义的包。</p>
<pre><code class="lang-lua">add_requires("zlib", "polarssl")
target("test")
    add_packages("zlib", "polarssl")
</code></pre>
<p>v2.2.3之后，还支持覆写内置的links，控制实际链接的库：</p>
<pre><code class="lang-lua">-- 默认会有 ncurses, panel, form等links
add_requires("ncurses")

target("test")

    -- 显示指定，只使用ncurses一个链接库
    add_packages("ncurses", {links = "ncurses"})
</code></pre>
<p>或者干脆禁用links，只使用头文件：</p>
<pre><code class="lang-lua">add_requires("lua")
target("test")
    add_packages("lua", {links = {}})
</code></pre>
<h3 id="targetadd_languages">target:add_languages</h3>
<h4 id="">添加语言标准</h4>
<p>与<a href="#targetset_languages">set_languages</a>类似，唯一区别是这个接口不会覆盖掉之前的设置，而是追加设置。</p>
<h3 id="targetadd_vectorexts">target:add_vectorexts</h3>
<h4 id="">添加向量扩展指令</h4>
<p>添加扩展指令优化选项，目前支持以下几种扩展指令集：</p>
<pre><code class="lang-lua">add_vectorexts("mmx")
add_vectorexts("neon")
add_vectorexts("avx", "avx2", "avx512")
add_vectorexts("sse", "sse2", "sse3", "ssse3", "sse4.2")
</code></pre>
<p>!> 如果当前设置的指令集编译器不支持，xmake会自动忽略掉，所以不需要用户手动去判断维护，只需要将你需要的指令集全部设置上就行了。</p>
<p>2.8.2 新增了一个 <code>all</code> 配置项，可以用于尽可能的开启所有扩展指令优化。</p>
<pre><code class="lang-lua">add_vectorexts("all")
</code></pre>
<h3 id="targetadd_frameworks">target:add_frameworks</h3>
<h4 id="">添加链接框架</h4>
<p>目前主要用于<code>ios</code>和<code>macosx</code>平台的<code>objc</code>和<code>swift</code>程序，例如：</p>
<pre><code class="lang-lua">target("test")
    add_frameworks("Foundation", "CoreFoundation")
</code></pre>
<p>当然也可以使用<a href="#targetadd_mxflags">add_mxflags</a>和<a href="#targetadd_ldflags">add_ldflags</a>来设置，不过比较繁琐，不建议这样设置。</p>
<pre><code class="lang-lua">target("test")
    add_mxflags("-framework Foundation", "-framework CoreFoundation")
    add_ldflags("-framework Foundation", "-framework CoreFoundation")
</code></pre>
<p>如果不是这两个平台，这些设置将会被忽略。</p>
<h3 id="targetadd_frameworkdirs">target:add_frameworkdirs</h3>
<h4 id="">添加链接框架搜索目录</h4>
<p>对于一些第三方framework，那么仅仅通过<a href="#targetadd_frameworks">add_frameworks</a>是没法找到的，还需要通过这个接口来添加搜索目录。</p>
<pre><code class="lang-lua">target("test")
    add_frameworks("MyFramework")
    add_frameworkdirs("/tmp/frameworkdir", "/tmp/frameworkdir2")
</code></pre>
<h3 id="targetset_toolset">target:set_toolset</h3>
<h4 id="">设置工具集</h4>
<p>针对特定target单独设置切换某个编译器，链接器，不过我们更推荐使用<a href="#targetset_toolchains">set_toolchains</a>对某个target进行整体工具链的切换。</p>
<p>与set_toolchains相比，此接口只切换工具链某个特定的编译器或者链接器。</p>
<p>!> 2.3.4以上版本才支持此接口，2.3.4之前的set_toolchain/set_tool接口会逐步弃用，采用此新接口，用法相同。</p>
<p>对于<code>add_files("*.c")</code>添加的源码文件，默认都是会调用系统最匹配的编译工具去编译，或者通过<code>xmake f --cc=clang</code>命令手动去修改，不过这些都是全局影响所有target目标的。</p>
<p>如果有些特殊需求，需要对当前工程下某个特定的target目标单独指定不同的编译器、链接器或者特定版本的编译器，这个时候此接口就可以排上用途了，例如：</p>
<pre><code class="lang-lua">target("test1")
    add_files("*.c")

target("test2")
    add_files("*.c")
    set_toolset("cc", "$(projectdir)/tools/bin/clang-5.0")
</code></pre>
<p>上述描述仅对test2目标的编译器进行特殊设置，使用特定的clang-5.0编译器来编译test2，而test1还是使用默认设置。</p>
<p><p class="tip"><br>每次设置都会覆盖当前target目标下之前的那次设置，不同target之间不会被覆盖，互相独立，如果在根域设置，会影响所有子target。<br></p>

</p>
<p>前一个参数是key，用于指定工具类型，目前支持的有（编译器、链接器、归档器）：</p>
<table>
<thead>
<tr>
<th>工具类型</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>cc</td>
<td>c编译器</td>
</tr>
<tr>
<td>cxx</td>
<td>c++编译器</td>
</tr>
<tr>
<td>mm</td>
<td>objc编译器</td>
</tr>
<tr>
<td>mxx</td>
<td>objc++编译器</td>
</tr>
<tr>
<td>gc</td>
<td>go编译器</td>
</tr>
<tr>
<td>as</td>
<td>汇编器</td>
</tr>
<tr>
<td>sc</td>
<td>swift编译器</td>
</tr>
<tr>
<td>rc</td>
<td>rust编译器</td>
</tr>
<tr>
<td>dc</td>
<td>dlang编译器</td>
</tr>
<tr>
<td>fc</td>
<td>fortran编译器</td>
</tr>
<tr>
<td>sc</td>
<td>swift编译器</td>
</tr>
<tr>
<td>rust</td>
<td>rust编译器</td>
</tr>
<tr>
<td>strip</td>
<td>strip程序</td>
</tr>
<tr>
<td>ld</td>
<td>c/c++/asm/objc等通用可执行程序链接器</td>
</tr>
<tr>
<td>sh</td>
<td>c/c++/asm/objc等通用动态库链接器</td>
</tr>
<tr>
<td>ar</td>
<td>c/c++/asm/objc等通用静态库归档器</td>
</tr>
<tr>
<td>dcld</td>
<td>dlang可执行链接器, rcld/gcld等类似</td>
</tr>
<tr>
<td>dcsh</td>
<td>dlang动态库链接器, rcsh/gcsh等类似</td>
</tr>
</tbody>
</table>
<p>对于一些编译器文件名不规则，导致xmake无法正常识别处理为已知的编译器名的情况下，我们也可以加一个工具名提示，例如：</p>
<pre><code class="lang-lua">set_toolset("cc", "gcc@$(projectdir)/tools/bin/mipscc.exe")
</code></pre>
<p>上述描述设置mipscc.exe作为c编译器，并且提示xmake作为gcc的传参处理方式进行编译。</p>
<h3 id="targetset_toolchains">target:set_toolchains</h3>
<h4 id="">设置工具链</h4>
<p>这对某个特定的target单独切换设置不同的工具链，和set_toolset不同的是，此接口是对完整工具链的整体切换，比如cc/ld/sh等一系列工具集。</p>
<p>这也是推荐做法，因为像gcc/clang等大部分编译工具链，编译器和链接器都是配套使用的，要切就得整体切，单独零散的切换设置会很繁琐。</p>
<p>比如我们切换test目标到clang+yasm两个工具链：</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("src/*.c")
    set_toolchains("clang", "yasm")
</code></pre>
<p>只需要指定工具链名字即可，具体xmake支持哪些工具链，可以通过下面的命令查看：</p>
<pre><code class="lang-bash">$ xmake show -l toolchains
xcode         Xcode IDE
vs            VisualStudio IDE
yasm          The Yasm Modular Assembler
clang         A C language family frontend for LLVM
go            Go Programming Language Compiler
dlang         D Programming Language Compiler
sdcc          Small Device C Compiler
cuda          CUDA Toolkit
ndk           Android NDK
rust          Rust Programming Language Compiler
llvm          A collection of modular and reusable compiler and toolchain technologies
cross         Common cross compilation toolchain
nasm          NASM Assembler
gcc           GNU Compiler Collection
mingw         Minimalist GNU for Windows
gnu-rm        GNU Arm Embedded Toolchain
envs          Environment variables toolchain
fasm          Flat Assembler
</code></pre>
<p>当然，我们也可以通过命令行全局切换到其他工具链：</p>
<pre><code class="lang-bash">$ xmake f --toolchain=clang
$ xmake
</code></pre>
<p>另外，我们也可以在xmake.lua中自定义toolchain，然后通过<code>set_toolchains</code>指定进去，例如：</p>
<pre><code class="lang-lua">toolchain("myclang")
    set_kind("standalone")
    set_toolset("cc", "clang")
    set_toolset("cxx", "clang", "clang++")
    set_toolset("ld", "clang++", "clang")
    set_toolset("sh", "clang++", "clang")
    set_toolset("ar", "ar")
    set_toolset("ex", "ar")
    set_toolset("strip", "strip")
    set_toolset("mm", "clang")
    set_toolset("mxx", "clang", "clang++")
    set_toolset("as", "clang")

    -- ...
</code></pre>
<p>关于这块的详情介绍，可以到<a href="/mirror/zh-cn/manual/custom_toolchain.html">自定义工具链</a>章节查看</p>
<p>更多详情见：<a href="https://github.com/xmake-io/xmake/issues/780">#780</a></p>
<p>2.3.5版本开始，新增对toolchains平台和架构的单独设置和切换，比如：</p>
<pre><code class="lang-lua">target("test")
    set_toolchains("xcode", {plat = os.host(), arch = os.arch()})
</code></pre>
<p>如果当前是在交叉编译模式，那么这个test还是会强制切到xcode的本地编译工具链和对应的pc平台上去，这对于想要同时支持部分target使用主机工具链，部分target使用交叉编译工具链时候，非常有用。</p>
<p>但是，这还不是特别方便，尤其是跨平台编译时候，不同平台的pc工具链都是不同的，有msvc, xcode, clang等，还需要判断平台来指定。</p>
<p>因此，我们可以直接使用<a href="#targetset_plat">set_plat</a>和<a href="#targetset_arch">set_arch</a>接口，直接设置特定target到主机平台，就可以内部自动选择host工具链了，例如：</p>
<pre><code class="lang-lua">target("test")
    set_plat(os.host())
    set_arch(os.arch())
</code></pre>
<p>这块的应用场景和example可以看下：<a href="https://github.com/xmake-io/xmake-repo/blob/dev/packages/l/luajit/port/xmake.lua">https://github.com/xmake-io/xmake-repo/blob/dev/packages/l/luajit/port/xmake.lua</a></p>
<p>luajit里面就需要同时编译host平台的minilua/buildvm来生成jit相关代码，然后开始针对性编译luajit自身到不同的交叉工具链。</p>
<p>关于这块详情，可以参考：<a href="https://github.com/xmake-io/xmake/pull/857">https://github.com/xmake-io/xmake/pull/857</a></p>
<p>v2.5.1 对 set_toolchains 做了进一步的改进，更好地对特定 target 支持独立工具链切换，比如不同 target 支持切换到不同的 vs 版本，例如：</p>
<pre><code class="lang-lua">target("test")
    set_toolchains("msvc", {vs = "2015"})
</code></pre>
<p>默认 xmake 会使用全局 vs 工具链，比如当前检测到 vs2019，但是用户同时还安装了 vs2015，那么可以通过上面的配置将 test 目标切换到 vs2015 来编译。</p>
<p>甚至还可以配合 <code>set_arch</code> 来指定特定的架构到 x86，而不是默认的 x64。</p>
<pre><code class="lang-lua">target("test")
    set_arch("x86")
    set_toolchains("msvc", {vs = "2015"})
</code></pre>
<p>上面的效果跟 <code>set_toolchains("msvc", {vs = "2015", arch = "x86"})</code> 类似，不过 <code>set_arch</code> 是针对 target 粒度的，而 <code>set_toolchains</code> 里面的 arch 设置仅仅针对特定工具链粒度。</p>
<p>通常，我们更推荐使用 <code>set_arch</code> 来对整个target实现架构切换。</p>
<h3 id="targetset_plat">target:set_plat</h3>
<h4 id="">设置指定目标的编译平台</h4>
<p>通常配合<a href="#targetset_arch">set_arch</a>使用，将指定target的编译平台切换到指定平台，xmake会自动根据切换的平台，选择合适的工具链。</p>
<p>一般用于需要同时编译host平台目标、交叉编译目标的场景，更多详情见：<a href="#targetset_toolchains">set_toolchains</a></p>
<p>例如：</p>
<pre><code class="lang-console">$ xmake f -p android --ndk=/xxx
</code></pre>
<p>即使正在使用android ndk编译android平台目标，但是其依赖的host目标，还是会切换到主机平台，使用xcode, msvc等host工具链来编译。</p>
<pre><code class="lang-lua">target("host")
    set_kind("binary")
    set_plat(os.host())
    set_arch(os.arch())
    add_files("src/host/*.c")

target("test")
    set_kind("binary")
    add_deps("host")
    add_files("src/test/*.c")
</code></pre>
<h3 id="targetset_arch">target:set_arch</h3>
<h4 id="">设置指定目标的编译架构</h4>
<p>详情见：<a href="#targetset_plat">set_plat</a></p>
<h3 id="targetset_values">target:set_values</h3>
<h4 id="">设置一些扩展配置值</h4>
<p>给target设置一些扩展的配置值，这些配置没有像<code>set_ldflags</code>这种内置的api可用，通过第一个参数传入一个配置名，来扩展配置。<br>一般用于传入配置参数给自定义rule中的脚本使用，例如：</p>
<pre><code class="lang-lua">rule("markdown")
    on_build_file(function (target, sourcefile, opt)
        -- compile .markdown with flags
        local flags = target:values("markdown.flags")
        if flags then
            -- ..
        end
    end)

target("test")
    add_files("src/*.md", {rule = "markdown"})
    set_values("markdown.flags", "xxx", "xxx")
</code></pre>
<p>上述代码例子中，可以看出，在target应用markdown规则的时候，通过set_values去设置一些flags值，提供给markdown规则去处理。<br>在规则脚本中可以通过<code>target:values("markdown.flags")</code>获取到target中设置的扩展flags值。</p>
<p>!> 具体扩展配置名，根据不同的rule，会有所不同，目前有哪些，可以参考相关规则的描述：<a href="/mirror/zh-cn/manual/custom_rule.html#内建规则">内建规则</a></p>
<p>下面是一些 xmake 目前支持的一些内置的扩展配置项列表。</p>
<table>
<thead>
<tr>
<th>扩展配置名</th>
<th>配置描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>fortran.moduledir</td>
<td>设置 fortran 模块的输出目录</td>
</tr>
<tr>
<td>ndk.arm_mode</td>
<td>设置 ndk 的 arm 编译模式（arm/thumb）</td>
</tr>
<tr>
<td>objc.build.arc</td>
<td>设置启用或禁用 objc 的 arc</td>
</tr>
<tr>
<td>objc++.build.arc</td>
<td>设置启用或禁用 objc++ 的 arc</td>
</tr>
<tr>
<td>xcode.bundle_identifier</td>
<td>设置 xcode 工具链的 Bundle Identifier</td>
</tr>
<tr>
<td>xcode.mobile_provision</td>
<td>设置 xcode 工具链的证书信息</td>
</tr>
<tr>
<td>xcode.codesign_identity</td>
<td>设置 xcode 工具链的代码签名标识</td>
</tr>
<tr>
<td>wasm.preloadfiles</td>
<td>设置 wasm 打包的预加载文件（preload file）</td>
</tr>
<tr>
<td>wdk.env.winver</td>
<td>设置 wdk 的 win 支持版本</td>
</tr>
<tr>
<td>wdk.umdf.sdkver</td>
<td>设置 wdk 的 umdf sdk 版本</td>
</tr>
<tr>
<td>wdk.kmdf.sdkver</td>
<td>设置 wdk 的 kmdf sdk 版本</td>
</tr>
<tr>
<td>wdk.sign.mode</td>
<td>设置 wdk 的代码签名模式</td>
</tr>
<tr>
<td>wdk.sign.store</td>
<td>设置 wdk 的代码签名 store</td>
</tr>
<tr>
<td>wdk.sign.certfile</td>
<td>设置 wdk 的代码签名证书文件</td>
</tr>
<tr>
<td>wdk.sign.thumbprint</td>
<td>设置 wdk 的代码签名指纹</td>
</tr>
</tbody>
</table>
<h3 id="targetadd_values">target:add_values</h3>
<h4 id="">添加一些扩展配置值</h4>
<p>用法跟<a href="#targetset_values">target:set_values</a>类似，区别就是这个接口是追加设置，而不会每次覆盖设置。</p>
<h3 id="targetset_rundir">target:set_rundir</h3>
<h4 id="">设置运行目录</h4>
<p>此接口用于设置默认运行target程序的当前运行目录，如果不设置，默认情况下，target是在可执行文件所在目录加载运行。</p>
<p>如果用户想要修改加载目录，一种是通过<code>on_run()</code>的方式自定义运行逻辑，里面去做切换，但仅仅为了切个目录就这么做，太过繁琐。</p>
<p>因此可以通过这个接口快速的对默认执行的目录环境做设置切换。</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("src/*.c")
    set_rundir("$(projectdir)/xxx")
</code></pre>
<h3 id="targetset_runargs">target:set_runargs</h3>
<h4 id="">设置运行参数列表</h4>
<p>2.6.9 新增接口，可用于设置 <code>xmake run</code> 的默认运行参数，通过它，我们可以避免每次命令行输入运行参数，<code>xmake run -x --arg1=val</code></p>
<pre><code class="lang-lua">set_runargs("-x", "--arg1=val")
</code></pre>
<h3 id="targetadd_runenvs">target:add_runenvs</h3>
<h4 id="">添加运行环境变量</h4>
<p>此接口用于添加设置默认运行target程序的环境变量，跟<a href="#targetset_runenv">set_runenv</a>不同的是，此接口是对已有系统env中的值进行追加，并不会覆盖。</p>
<p>所以，对于PATH这种，通过此接口追加值是非常方便的，而且此接口支持多值设置，所以通常就是用来设置带有path sep的多值env。。</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("src/*.c")
    add_runenvs("PATH", "/tmp/bin", "xxx/bin")
    add_runenvs("LD_LIBRARY_PATH", "/tmp/lib", "xxx/lib")
</code></pre>
<h3 id="targetset_runenv">target:set_runenv</h3>
<h4 id="">设置运行环境变量</h4>
<p>此接口跟<a href="#targetadd_runenvs">add_runenvs</a>不同的是，<code>set_runenv</code>是对某个环境变量的覆盖设置，会覆盖原有系统环境的env值，并且此接口是单数设置，不能传递多参。</p>
<p>所以，如果要覆盖设置PATH这中多路径的env，需要自己去拼接：</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("src/*.c")
    set_runenv("PATH", path.joinenv("/tmp/bin", "xxx/bin"))
    set_runenv("NAME", "value")
</code></pre>
<h3 id="targetset_installdir">target:set_installdir</h3>
<h4 id="">设置安装目录</h4>
<p>2.2.5版本新增接口，用于针对每个target设置不同的默认安装目录，一般用于<code>xmake install/uninstall</code>命令。</p>
<p>默认情况下执行<code>xmake install</code>会安装到系统<code>/usr/local</code>目录，我们除了可以通过<code>xmake install -o /usr/local</code>指定其他安装目录外，<br>还可以在xmake.lua中针对target设置不同的安装目录来替代默认目录。</p>
<p>除了上述两种方式，我们也可以通过<code>INSTALLDIR</code>和<code>DESTDIR</code>环境变量设置默认的安装目录。</p>
<h3 id="targetset_prefixdir">target:set_prefixdir</h3>
<h4 id="">设置安装前置子目录</h4>
<p>尽管通过 <code>set_installdir</code> 和 <code>xmake install -o [installdir]</code> 设置了安装根目录，但是如果我们还想进一步调整 bin, lib 和 include 的子路径。</p>
<p>那么，我们可以使用这个接口，默认情况下，安装目录会按照这个结构：</p>
<pre><code class="lang-bash">installdir
  - bin
  - lib
  - include
</code></pre>
<p>如果我们配置：</p>
<pre><code class="lang-lua">set_prefix("prefixdir")
</code></pre>
<p>就是增加一个总的子目录：</p>
<pre><code class="lang-bash">installdir
  - prefixdir
    - bin
    - lib
    - include
</code></pre>
<p>我们还可以单独配置 bin, lib 和 include 子目录，例如：</p>
<pre><code class="lang-lua">set_prefix("prefixdir", {bindir = "mybin", libdir = "mylib", includedir = "myinc"})
</code></pre>
<pre><code class="lang-bash">installdir
  - prefixdir
    - mybin
    - mylib
    - myinc
</code></pre>
<p>如果，我们不配置 prefixdir，仅仅修改 bin 子目录，可以将 prefixdir 配置成 <code>/</code>。</p>
<pre><code class="lang-lua">set_prefix("/", {bindir = "mybin", libdir = "mylib", includedir = "myinc"})
</code></pre>
<pre><code class="lang-bash">installdir
  - mybin
  - mylib
  - myinc
</code></pre>
<h3 id="targetadd_installfiles">target:add_installfiles</h3>
<h4 id="">添加安装文件</h4>
<p>2.2.5版本新增接口，用于针对每个target设置对应需要安装的文件，一般用于<code>xmake install/uninstall</code>命令。</p>
<p>比如我们可以指定安装各种类型的文件到安装目录：</p>
<pre><code class="lang-lua">target("test")
    add_installfiles("src/*.h")
    add_installfiles("doc/*.md")
</code></pre>
<p>默认在linux等系统上，我们会安装到<code>/usr/local/*.h, /usr/local/*.md</code>，不过我们也可以指定安装到特定子目录：</p>
<pre><code class="lang-lua">target("test")
    add_installfiles("src/*.h", {prefixdir = "include"})
    add_installfiles("doc/*.md", {prefixdir = "share/doc"})
</code></pre>
<p>上面的设置，我们会安装到<code>/usr/local/include/*.h, /usr/local/share/doc/*.md</code></p>
<p>注：默认安装不会保留目录结构，会完全展开，当然我们也可以通过<code>()</code>去提取源文件中的子目录结构来安装，例如：</p>
<pre><code class="lang-lua">target("test")
    add_installfiles("src/(tbox/*.h)", {prefixdir = "include"})
    add_installfiles("doc/(tbox/*.md)", {prefixdir = "share/doc"})
</code></pre>
<p>我们把<code>src/tbox/*.h</code>中的文件，提取<code>tbox/*.h</code>子目录结构后，在进行安装：<code>/usr/local/include/tbox/*.h, /usr/local/share/doc/tbox/*.md</code></p>
<p>当然，用户也可以通过<a href="#targetset_installdir">set_installdir</a>接口，来配合使用。</p>
<p>关于此接口的详细说明，见：<a href="https://github.com/xmake-io/xmake/issues/318">https://github.com/xmake-io/xmake/issues/318</a></p>
<h3 id="targetadd_headerfiles">target:add_headerfiles</h3>
<h4 id="">添加安装头文件</h4>
<p>2.2.5版本新增接口，用于针对每个target设置对应需要安装的头文件，一般用于<code>xmake install/uninstall</code>命令。</p>
<p>此接口使用方式跟<a href="#targetadd_installfiles">add_installfiles</a>接口几乎完全一样，都可以用来添加安装文件，不过此接口仅用于安装头文件。<br>因此，使用上比<code>add_installfiles</code>简化了不少，默认不设置prefixdir，也会自动将头文件安装到对应的<code>include</code>子目录中。</p>
<p>并且此接口对于<code>xmake project -k vs201x</code>等插件生成的IDE文件，也会添加对应的头文件进去。</p>
<p>我注：默认安装不会保留目录结构，会完全展开，当然们也可以通过<code>()</code>去提取源文件中的子目录结构来安装，例如：</p>
<pre><code class="lang-lua">target("test")
    add_headerfiles("src/(tbox/*.h)", {prefixdir = "include"})
</code></pre>
<p>v2.7.1 之后，我们可以通过 <code>{install = false}</code> 参数，禁用默认的头文件安装行为，仅仅对设置的头文件用于 project generator 的文件列表展示和编辑，例如 vs project。</p>
<pre><code class="lang-lua">add_headerfiles("src/foo.h")
add_headerfiles("src/test.h", {install = false})
</code></pre>
<p>上面两个头文件，在 vs 工程中都会展示出来，但是仅仅 foo.h 会被发布安装到系统。</p>
<h3 id="targetset_configdir">target:set_configdir</h3>
<h4 id="">设置模板配置文件的输出目录</h4>
<p>2.2.5版本新增接口，主要用于<a href="#targetadd_configfiles">add_configfiles</a>接口设置的模板配置文件的输出目录。</p>
<h3 id="targetset_configvar">target:set_configvar</h3>
<h4 id="">设置模板配置变量</h4>
<p>2.2.5版本新增接口，用于在编译前，添加一些需要预处理的模板配置变量，一般用于<a href="#targetadd_configfiles">add_configfiles</a>接口。</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("main.c")
    set_configvar("HAS_FOO", 1)
    set_configvar("HAS_BAR", "bar")
    set_configvar("HAS_ZOO", "zoo", {quote = false})
    add_configfiles("config.h.in")
</code></pre>
<p>config.h.in</p>
<pre><code class="lang-c">${define HAS_FOO}
${define HAS_BAR}
${define HAS_ZOO}
</code></pre>
<p>生成的 config.h 内容如下：</p>
<pre><code class="lang-c">#define HAS_FOO 1
#define HAS_BAR "bar"
#define HAS_ZOO zoo
</code></pre>
<p>set_configvar 可以设置 number，string 和 boolean 类型值，如果是 string 值，默认生成的宏定义带有引号，如果要去掉引号，可以设置 <code>{quote = false}</code>。</p>
<p>相关 issues 见：<a href="https://github.com/xmake-io/xmake/issues/1694">#1694</a></p>
<p>对于，宏定义里面有路径，需要转义处理路径分隔符的，我们也可以配置开启路径字符转义。</p>
<pre><code class="lang-lua">set_configvar("TEST", "C:\\hello", {escape = true})
</code></pre>
<p>它会自动转义成 <code>#define TEST "C:\\hello"</code> ，如果没开启转义，则会变成：<code>#define TEST "C:\hello"</code></p>
<p>相关 issues 见：<a href="https://github.com/xmake-io/xmake/issues/1872">#1872</a></p>
<h3 id="targetadd_configfiles">target:add_configfiles</h3>
<h4 id="">添加模板配置文件</h4>
<p>2.2.5版本新增接口，用于在编译前，添加一些需要预处理的配置文件。</p>
<p>先来一个简单的例子：</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("src/*.c")
    set_configdir("$(buildir)/config")
    add_configfiles("src/config.h.in")
</code></pre>
<p>上面的设置，会在编译前，自动的将<code>config.h.in</code>这个头文件配置模板，经过预处理后，生成输出到指定的<code>build/config/config.h</code>。</p>
<p>如果<code>set_configdir</code>不设置，那么默认输出到<code>build</code>目录下。</p>
<p>其中<code>.in</code>后缀会被自动识别处理掉，如果想要输出存储为其他文件名，可以通过：</p>
<pre><code class="lang-lua">add_configfiles("src/config.h", {filename = "myconfig.h"})
</code></pre>
<p>的方式，来重命名输出，同样，这个接口跟<a href="#targetadd_configfiles">add_installfiles</a>类似，也是支持prefixdir和子目录提取设置：</p>
<pre><code class="lang-lua">add_configfiles("src/*.h.in", {prefixdir = "subdir"})
add_configfiles("src/(tbox/config.h)")
</code></pre>
<h5 id="">变量替换</h5>
<p>这个接口的一个最重要的特性就是，可以在预处理的时候，对里面的一些模板变量进行预处理替换，例如：</p>
<p>config.h.in</p>
<pre><code>#define VAR1 "${VAR1}"
#define VAR2 "${VAR2}"
#define HELLO "${HELLO}"
</code></pre><pre><code class="lang-lua">set_configvar("VAR1", "1")

target("test")
    set_kind("binary")
    add_files("main.c")

    set_configvar("VAR2", 2)
    add_configfiles("config.h.in", {variables = {hello = "xmake"}})
    add_configfiles("*.man", {onlycopy = true})
</code></pre>
<p>通过<a href="#targetset_configvar">set_configvar</a>接口设置模板变量，裹着通过<code>{variables = {xxx = ""}}</code>中设置的变量进行替换处理。</p>
<p>预处理后的文件<code>config.h</code>内容为：</p>
<pre><code>#define VAR1 "1"
#define VAR2 "2"
#define HELLO "xmake"
</code></pre><p>而<code>{onlycopy = true}</code>设置，会强制将<code>*.man</code>作为普通文件处理，仅在预处理阶段copy文件，不进行变量替换。</p>
<p>默认的模板变量匹配模式为<code>${var}</code>，当然我们也可以设置其他的匹配模式，例如，改为<code>@var@</code>匹配规则：</p>
<pre><code class="lang-lua">target("test")
    add_configfiles("config.h.in", {pattern = "@(.-)@"})
</code></pre>
<h5 id="">内置变量</h5>
<p>我们也有提供了一些内置的变量，即使不通过此接口设置，也是可以进行默认变量替换的：</p>
<pre><code>${VERSION} -> 1.6.3
${VERSION_MAJOR} -> 1
${VERSION_MINOR} -> 6
${VERSION_ALTER} -> 3
${VERSION_BUILD} -> set_version("1.6.3", {build = "%Y%m%d%H%M"}) -> 201902031421
${PLAT} and ${plat} -> MACOS and macosx
${ARCH} and ${arch} -> ARM and arm
${MODE} and ${mode} -> DEBUG/RELEASE and debug/release
${DEBUG} and ${debug} -> 1 or 0
${OS} and ${os} -> IOS or ios
</code></pre><p>例如：</p>
<p>config.h.in</p>
<pre><code class="lang-c">#define CONFIG_VERSION "${VERSION}"
#define CONFIG_VERSION_MAJOR ${VERSION_MAJOR}
#define CONFIG_VERSION_MINOR ${VERSION_MINOR}
#define CONFIG_VERSION_ALTER ${VERSION_ALTER}
#define CONFIG_VERSION_BUILD ${VERSION_BUILD}
</code></pre>
<p>config.h</p>
<pre><code class="lang-c">#define CONFIG_VERSION "1.6.3"
#define CONFIG_VERSION_MAJOR 1
#define CONFIG_VERSION_MINOR 6
#define CONFIG_VERSION_ALTER 3
#define CONFIG_VERSION_BUILD 201902031401
</code></pre>
<p>v2.5.3 后新增 git 相关内置变量：</p>
<pre><code class="lang-c">#define GIT_COMMIT      "${GIT_COMMIT}"
#define GIT_COMMIT_LONG "${GIT_COMMIT_LONG}"
#define GIT_COMMIT_DATE "${GIT_COMMIT_DATE}"
#define GIT_BRANCH      "${GIT_BRANCH}"
#define GIT_TAG         "${GIT_TAG}"
#define GIT_TAG_LONG    "${GIT_TAG_LONG}"
#define GIT_CUSTOM      "${GIT_TAG}-${GIT_COMMIT}"
</code></pre>
<pre><code class="lang-c">#define GIT_COMMIT      "8c42b2c2"
#define GIT_COMMIT_LONG "8c42b2c251793861eb85ffdf7e7c2307b129c7ae"
#define GIT_COMMIT_DATE "20210121225744"
#define GIT_BRANCH      "dev"
#define GIT_TAG         "v1.6.6"
#define GIT_TAG_LONG    "v1.6.6-0-g8c42b2c2"
#define GIT_CUSTOM      "v1.6.6-8c42b2c2"
</code></pre>
<h5 id="">宏定义</h5>
<p>我们还可以对<code>#define</code>定义进行一些变量状态控制处理：</p>
<p>config.h.in</p>
<pre><code class="lang-c">${define FOO_ENABLE}
</code></pre>
<pre><code class="lang-lua">set_configvar("FOO_ENABLE", 1) -- or pass true
set_configvar("FOO_STRING", "foo")
</code></pre>
<p>通过上面的变量设置后，<code>${define xxx}</code>就会替换成：</p>
<pre><code class="lang-c">#define FOO_ENABLE 1
#define FOO_STRING "foo"
</code></pre>
<p>或者（设置为0禁用的时候）</p>
<pre><code class="lang-c">/* #undef FOO_ENABLE */
/* #undef FOO_STRING */
</code></pre>
<p>这种方式，对于一些自动检测生成config.h非常有用，比如配合option来做自动检测：</p>
<pre><code class="lang-lua">option("foo")
    set_default(true)
    set_description("Enable Foo")
    set_configvar("FOO_ENABLE", 1) -- 或者传递true，启用FOO_ENABLE变量
    set_configvar("FOO_STRING", "foo")

target("test")
    add_configfiles("config.h.in")

    -- 如果启用foo选项 -> 添加 FOO_ENABLE 和 FOO_STRING 定义
    add_options("foo")
</code></pre>
<p>config.h.in</p>
<pre><code class="lang-c">${define FOO_ENABLE}
${define FOO_STRING}
</code></pre>
<p>config.h</p>
<pre><code class="lang-c">#define FOO_ENABLE 1
#define FOO_STRING "foo"
</code></pre>
<p>关于option选项检测，以及config.h的自动生成，有一些辅助函数，可以看下：<a href="https://github.com/xmake-io/xmake/issues/342">https://github.com/xmake-io/xmake/issues/342</a></p>
<p>除了<code>#define</code>，如果想要对其他非<code>#define xxx</code>也做状态切换处理，可以使用 <code>${default xxx 0}</code> 模式，设置默认值，例如：</p>
<pre><code>HAVE_SSE2 equ ${default VAR_HAVE_SSE2 0}
</code></pre><p>通过<code>set_configvar("HAVE_SSE2", 1)</code>启用变量后，变为<code>HAVE_SSE2 equ 1</code>，如果没有设置变量，则使用默认值：<code>HAVE_SSE2 equ 0</code></p>
<p>关于这个的详细说明，见：<a href="https://github.com/xmake-io/xmake/issues/320">https://github.com/xmake-io/xmake/issues/320</a></p>
<h3 id="targetset_policy">target:set_policy</h3>
<h4 id="">设置构建行为策略</h4>
<p>xmake有很多的默认行为，比如：自动检测和映射flags、跨target并行构建等，虽然提供了一定的智能化处理，但重口难调，不一定满足所有的用户的使用习惯和需求。</p>
<p>因此，从v2.3.4开始，xmake提供默认构建策略的修改设置，开放给用户一定程度上的可配置性。</p>
<p>使用方式如下：</p>
<pre><code class="lang-lua">set_policy("check.auto_ignore_flags", false)
</code></pre>
<p>只需要在项目根域设置这个配置，就可以禁用flags的自动检测和忽略机制，另外<code>set_policy</code>也可以针对某个特定的target局部生效。</p>
<pre><code class="lang-lua">target("test")
    set_policy("check.auto_ignore_flags", false)
</code></pre>
<p>完整的 policies 支持列表和使用说明，见：<a href="/mirror/zh-cn/guide/build_policies.html">构建策略</a></p>
<h3 id="targetset_runtimes">target:set_runtimes</h3>
<h4 id="">设置编译目标依赖的运行时库</h4>
<p>这是 v2.5.1 开始新增的接口，用于抽象化设置编译目标依赖的运行时库，目前仅仅支持对 msvc 运行时库的抽象，但后续也许会扩展对其他编译器运行时库的映射。</p>
<p>目前支持的一些配置值说明如下：</p>
<table>
<thead>
<tr>
<th>值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>MT</td>
<td>msvc 运行时库：多线程静态库</td>
</tr>
<tr>
<td>MTd</td>
<td>msvc 运行时库：多线程静态库（调试）</td>
</tr>
<tr>
<td>MD</td>
<td>msvc 运行时库：多线程动态库</td>
</tr>
<tr>
<td>MDd</td>
<td>msvc 运行时库：多线程动态库（调试）</td>
</tr>
<tr>
<td>c++_static</td>
<td>clang 的 c++ 运行时库，静态库</td>
</tr>
<tr>
<td>c++_shared</td>
<td>clang 的 c++ 运行时库，动态库</td>
</tr>
<tr>
<td>stdc++_static</td>
<td>gcc 的 c++ 运行时库，静态库</td>
</tr>
<tr>
<td>stdc++_shared</td>
<td>gcc 的 c++ 运行时库，动态库</td>
</tr>
<tr>
<td>gnustl_static</td>
<td>android 的 c++ 运行时库，静态库，高版本 NDK 已废弃</td>
</tr>
<tr>
<td>gnustl_shared</td>
<td>android 的 c++ 运行时库，静态库，高版本 NDK 已废弃</td>
</tr>
<tr>
<td>stlport_static</td>
<td>android 的 c++ 运行时库，静态库，高版本 NDK 已废弃</td>
</tr>
<tr>
<td>stlport_static</td>
<td>android 的 c++ 运行时库，静态库，高版本 NDK 已废弃</td>
</tr>
</tbody>
</table>
<p>关于 vs 运行时，可以参考：<a href="https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library?view=msvc-160">msvc 运行时说明</a></p>
<p>而这个接口传入 MT/MTd 参数配置，xmake 会自动配置上 <code>/MT /nodefaultlib:msvcrt.lib</code> 参数。</p>
<p>我们可以针对不同的 target 设置不同的运行时。</p>
<p>另外，如果我们将 <code>set_runtimes</code> 设置在全局根域，那么所有的 <code>add_requires("xx")</code> 包定义也会全局同步切换到对应的 vs runtime 配置</p>
<pre><code class="lang-lua">set_runtimes("MD")
add_requires("libcurl", "fmt")
target("test")
   set_kind("binary")
   add_files("src/*.c")
</code></pre>
<p>当然，我们也可以通过 <code>add_requires("xx", {configs = {vs_runtime = "MD"}})</code> 对特定包修改 vs 运行时库。</p>
<p>我们也可以通过 <code>xmake f --vs_runtime=&#39;MD&#39;</code> 通过参数配置来全局切换它。</p>
<p>与此 api 相关的 issue：<a href="https://github.com/xmake-io/xmake/issues/1071#issuecomment-750817681">#1071</a></p>
<h3 id="targetset_group">target:set_group</h3>
<h4 id="">设置目标分组</h4>
<h5 id="">用于工程文件分组展示</h5>
<p>此接口可用于 vs/vsxmake 工程生成，对 vs 工程内部子工程目录树按指定结构分组展示，不过后续也可能对其他模块增加分组支持。</p>
<p>比如对于下面的分组配置：</p>
<pre><code class="lang-lua">add_rules("mode.debug", "mode.release")

target("test1")
    set_kind("binary")
    add_files("src/*.cpp")
    set_group("group1")

target("test2")
    set_kind("binary")
    add_files("src/*.cpp")
    set_group("group1")

target("test3")
    set_kind("binary")
    add_files("src/*.cpp")
    set_group("group1/group2")

target("test4")
    set_kind("binary")
    add_files("src/*.cpp")
    set_group("group3/group4")

target("test5")
    set_kind("binary")
    add_files("src/*.cpp")

target("test6")
    set_kind("binary")
    add_files("src/*.cpp")
</code></pre>
<p>生成的 vs 工程目录结构效果如下：</p>
<p><img src="assets/img/manual/set_group.png" alt=""></p>
<p>其中 <code>set_group("group1/group2")</code> 可以将 target 设置到二级分组中去。</p>
<p>更多详情见：<a href="https://github.com/xmake-io/xmake/issues/1026">#1026</a></p>
<h5 id="">编译指定一批目标程序</h5>
<p>我们可以使用 <code>set_group()</code> 将给定的目标标记为 <code>test/benchmark/...</code> 并使用 <code>set_default(false)</code> 禁用来默认构建它。</p>
<p>然后，通过 <code>xmake -g xxx</code> 命令就能指定构建一批目标程序了。</p>
<p>比如，我们可以使用此功能来构建所有测试。</p>
<pre><code class="lang-lua">target("test1")
    set_kind("binary")
    set_default(false)
    set_group("test")
    add_files("src/*.cpp")

target("test2")
    set_kind("binary")
    set_default(false)
    set_group("test")
    add_files("src/*.cpp")
</code></pre>
<pre><code class="lang-console">$ xmake -g test
$ xmake --group=test
</code></pre>
<h5 id="">运行指定一批目标程序</h5>
<p>我们也可以通过设置分组，来指定运行所有带有 <code>test</code> 分组的测试程序。</p>
<pre><code class="lang-console">$ xmake run -g test
$ xmake run --group=test
</code></pre>
<p>另外，我们还可以支持分组的模式匹配：</p>
<pre><code>$ xmake build -g test_*
$ xmake run -g test/foo_*
$ xmake build -g bench*
$ xmake run -g bench*
</code></pre><p>更多信息见：<a href="https://github.com/xmake-io/xmake/issues/1913">#1913</a></p>
<h3 id="targetadd_filegroups">target:add_filegroups</h3>
<h4 id="">添加源文件分组</h4>
<p>这个接口目前主要用于对 vs/vsxmake/cmakelists generator 生成的工程文件进行源文件分组展示。</p>
<p>如果不设置分组展示，Xmake 也会默认按照树状模式展示，但是有些极端情况下，目录层级显示不是很好，例如：</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("../../../../src/**.cpp")
</code></pre>
<p><img src="https://xmake.io/assets/img/manual/filegroup1.png" alt=""></p>
<p>目前主要支持两种展示模式：</p>
<ul>
<li>plain: 平坦模式</li>
<li>tree: 树形展示，这也是默认模式</li>
</ul>
<p>另外，它也支持对 <code>add_headerfiles</code> 添加的文件进行分组。</p>
<h5 id="">设置分组并指定根目录</h5>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("../../../../src/**.cpp")
    add_filegroups("group1/group2", {rootdir = "../../../../"})
</code></pre>
<p><img src="https://xmake.io/assets/img/manual/filegroup2.png" alt=""></p>
<h5 id="">设置分组并指定文件匹配模式</h5>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("../../../../src/**.cpp")
    add_filegroups("group1/group2", {rootdir = "../../../../", files = {"src/**.cpp"}})
</code></pre>
<h5 id="">作为平坦模式展示</h5>
<p>这种模式下，所有源文件忽略嵌套的目录层级，在分组下同一层级展示。</p>
<pre><code class="lang-lua">target("test")
    set_kind("binary")
    add_files("../../../../src/**.cpp")
    add_filegroups("group1/group2", {rootdir = "../../../../", mode = "plain"})
</code></pre>
<p><img src="https://xmake.io/assets/img/manual/filegroup3.png" alt=""></p>
<h3 id="targetset_exceptions">target:set_exceptions</h3>
<h4 id="">启用或者禁用异常</h4>
<p>我们可以通过这个配置，配置启用和禁用 C++/Objc 的异常。</p>
<p>通常，如果我们通过 add_cxxflags 接口去配置它们，需要根据不同的平台，编译器分别处理它们，非常繁琐。</p>
<p>例如：</p>
<pre><code class="lang-lua">    on_config(function (target)
        if (target:has_tool("cxx", "cl")) then
            target:add("cxflags", "/EHsc", {force = true})
            target:add("defines", "_HAS_EXCEPTIONS=1", {force = true})
        elseif(target:has_tool("cxx", "clang") or target:has_tool("cxx", "clang-cl")) then
            target:add("cxflags", "-fexceptions", {force = true})
            target:add("cxflags", "-fcxx-exceptions", {force = true})
        end
    end)
</code></pre>
<p>而通过这个接口，我们就可以抽象化成编译器无关的方式去配置它们。</p>
<p>开启 C++ 异常:</p>
<pre><code class="lang-lua">set_exceptions("cxx")
</code></pre>
<p>禁用 C++ 异常:</p>
<pre><code class="lang-lua">set_exceptions("no-cxx")
</code></pre>
<p>我们也可以同时配置开启 objc 异常。</p>
<pre><code class="lang-lua">set_exceptions("cxx", "objc")
</code></pre>
<p>或者禁用它们。</p>
<pre><code class="lang-lua">set_exceptions("no-cxx", "no-objc")
</code></pre>
<p>Xmake 会在内部自动根据不同的编译器，去适配对应的 flags。</p>
<h3 id="targetset_encodings">target:set_encodings</h3>
<h4 id="">设置编码</h4>
<p>这是 2.8.2 版本新增的接口，我们可以用这个接口设置源文件、目标执行文件的编码。</p>
<p>目前支持的编码：utf-8, gb2312 (msvc)</p>
<p>默认情况下，我们仅仅指定编码，是会同时对源文件，目标文件生效。</p>
<pre><code class="lang-lua">-- for all source/target encodings
set_encodings("utf-8") -- msvc: /utf-8
</code></pre>
<p>它等价于：</p>
<pre><code class="lang-lua">set_encodings("source:utf-8", "target:utf-8")
</code></pre>
<p>并且，目前仅仅支持设置成 utf-8 编码，将来会不断扩展。</p>
<p>如果，我们仅仅想单独设置源文件编码，或者目标文件编码，也是可以的。</p>
<h5 id="">设置源文件编码</h5>
<p>通常指的是编译的代码源文件的编码，我们可以这么设置。</p>
<pre><code class="lang-lua">-- gcc/clang: -finput-charset=UTF-8, msvc: -source-charset=utf-8
set_encodings("source:utf-8")
</code></pre>
<h5 id="">设置目标文件编码</h5>
<p>它通常指的是目标可执行文件的运行输出编码。</p>
<pre><code class="lang-lua">-- gcc/clang: -fexec-charset=UTF-8, msvc: -target-charset=utf-8
set_encodings("target:utf-8")
</code></pre>
<h3 id="targetadd_forceincludes">target:add_forceincludes</h3>
<h4 id="includes">强制添加 includes</h4>
<p>这是 2.8.2 新增的接口，用于在配置文件中直接强制添加 <code>includes</code> 头文件。</p>
<pre><code class="lang-lua">add_forceincludes("config.h")
</code></pre>
<p>它的效果类似于 <code>#include <config.h></code>，但是不需要在源码中显式添加它了。</p>
<p>另外，它的搜索路径也是需要通过 <code>add_includedirs</code> 来控制，而不是直接配置文件路径。</p>
<pre><code class="lang-lua">add_forceincludes("config.h")
add_includedirs("src")
</code></pre>
<p>默认 add_forceincludes 匹配 c/c++/objc。如果仅仅只想匹配 c++ 可以这么配置：</p>
<pre><code class="lang-lua">add_forceincludes("config.h", {sourcekinds = "cxx"})
</code></pre>
<p>如果想同时匹配多个源文件类型，也是可以的：</p>
<pre><code class="lang-lua">add_forceincludes("config.h", {sourcekinds = {"cxx", "mxx"}})
</code></pre>
<h3 id="targetadd_extrafiles">target:add_extrafiles</h3>
<h4 id="">添加额外的文件</h4>
<p>这个接口也是 2.8.2 新加的，主要用于 vs/vsxmake project generator 生成的工程中，添加额外的文件到工程列表中去，这样，用户也可以快速点击编辑它们，尽管它们不是代码文件。</p>
<p>将来，我们也可能用此接口做更多其他的事情。</p>
<pre><code class="lang-lua">add_extrafiles("assets/other.txt")
</code></pre>
<h3 id="targetadd_tests">target:add_tests</h3>
<h4 id="">添加测试用例</h4>
<p>2.8.5 版本开始，我们增加了内置的测试命令：<code>xmake test</code>，我们只需要在需要测试的 target 上通过 add_tests 配置一些测试用例，就可以自动执行测试。</p>
<p>即使当前 target 被设置成了 <code>set_default(false)</code>，在执行测试的时候，xmake 也还是会先自动编译它们，然后自动运行所有的测试。</p>
<p>我们可以先看个整体的例子，大概知道下它是怎么样子的。</p>
<pre><code class="lang-lua">add_rules("mode.debug", "mode.release")

for _, file in ipairs(os.files("src/test_*.cpp")) do
    local name = path.basename(file)
    target(name)
        set_kind("binary")
        set_default(false)
        add_files("src/" .. name .. ".cpp")
        add_tests("default")
        add_tests("args", {runargs = {"foo", "bar"}})
        add_tests("pass_output", {trim_output = true, runargs = "foo", pass_outputs = "hello foo"})
        add_tests("fail_output", {fail_outputs = {"hello2 .*", "hello xmake"}})
end
</code></pre>
<p>这个例子，自动扫描源码目录下的 <code>test_*.cpp</code> 源文件，然后每个文件自动创建一个测试目标，它被设置成了 <code>set_default(false)</code>，也就是正常情况下，默认不会编译它们。</p>
<p>但是，如果执行 <code>xmake test</code> 进行测试，它们就会被自动编译，然后测试运行，运行效果如下：</p>
<pre><code class="lang-bash">ruki-2:test ruki$ xmake test
running tests ...
[  2%]: test_1/args        .................................... passed 7.000s
[  5%]: test_1/default     .................................... passed 5.000s
[  8%]: test_1/fail_output .................................... passed 5.000s
[ 11%]: test_1/pass_output .................................... passed 6.000s
[ 13%]: test_2/args        .................................... passed 7.000s
[ 16%]: test_2/default     .................................... passed 6.000s
[ 19%]: test_2/fail_output .................................... passed 6.000s
[ 22%]: test_2/pass_output .................................... passed 6.000s
[ 25%]: test_3/args        .................................... passed 7.000s
[ 27%]: test_3/default     .................................... passed 7.000s
[ 30%]: test_3/fail_output .................................... passed 6.000s
[ 33%]: test_3/pass_output .................................... passed 6.000s
[ 36%]: test_4/args        .................................... passed 6.000s
[ 38%]: test_4/default     .................................... passed 6.000s
[ 41%]: test_4/fail_output .................................... passed 5.000s
[ 44%]: test_4/pass_output .................................... passed 6.000s
[ 47%]: test_5/args        .................................... passed 5.000s
[ 50%]: test_5/default     .................................... passed 6.000s
[ 52%]: test_5/fail_output .................................... failed 6.000s
[ 55%]: test_5/pass_output .................................... failed 5.000s
[ 58%]: test_6/args        .................................... passed 7.000s
[ 61%]: test_6/default     .................................... passed 6.000s
[ 63%]: test_6/fail_output .................................... passed 6.000s
[ 66%]: test_6/pass_output .................................... passed 6.000s
[ 69%]: test_7/args        .................................... failed 6.000s
[ 72%]: test_7/default     .................................... failed 7.000s
[ 75%]: test_7/fail_output .................................... failed 6.000s
[ 77%]: test_7/pass_output .................................... failed 5.000s
[ 80%]: test_8/args        .................................... passed 7.000s
[ 83%]: test_8/default     .................................... passed 6.000s
[ 86%]: test_8/fail_output .................................... passed 6.000s
[ 88%]: test_8/pass_output .................................... failed 5.000s
[ 91%]: test_9/args        .................................... passed 6.000s
[ 94%]: test_9/default     .................................... passed 6.000s
[ 97%]: test_9/fail_output .................................... passed 6.000s
[100%]: test_9/pass_output .................................... passed 6.000s

80% tests passed, 7 tests failed out of 36, spent 0.242s
</code></pre>
<p><img src="/assets/img/manual/xmake-test1.png" alt=""></p>
<p>我们也可以执行 <code>xmake test -vD</code> 查看详细的测试失败的错误信息：</p>
<p><img src="/assets/img/manual/xmake-test2.png" alt=""></p>
<h5 id="">运行指定测试目标</h5>
<p>我们也可以指定运行指定 target 的某个测试：</p>
<pre><code class="lang-bash">$ xmake test targetname/testname
</code></pre>
<p>或者按模式匹配的方式，运行一个 target 的所有测试，或者一批测试：</p>
<pre><code class="lang-bash">$ xmake test targetname/*
$ xmake test targetname/foo*
</code></pre>
<p>也可以运行所有 target 的同名测试：</p>
<pre><code class="lang-bash">$ xmake test */testname
</code></pre>
<h5 id="">并行化运行测试</h5>
<p>其实，默认就是并行化运行的，但是我们可以通过 <code>-jN</code> 调整运行的并行度。</p>
<pre><code class="lang-bash">$ xmake test -jN
</code></pre>
<h5 id="">分组运行测试</h5>
<pre><code class="lang-bash">$ xmake test -g "foo"
$ xmake test -g "foo*"
</code></pre>
<h5 id="">添加测试到目标（无参数）</h5>
<p>如果没有配置任何参数，仅仅配置了测试名到 <code>add_tests</code>，那么仅仅测试这个目标程序的是否会运行失败，根据退出代码来判断是否通过测试。</p>
<pre><code>target("test")
    add_tests("testname")
</code></pre><h5 id="">配置运行参数</h5>
<p>我们也可以通过 <code>{runargs = {"arg1", "arg2"}}</code> 的方式，给 <code>add_tests</code> 配置指定测试需要运行的参数。</p>
<p>另外，一个 target 可以同时配置多个测试用例，每个测试用例可独立运行，互不冲突。</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname", {runargs = "arg1"})
    add_tests("testname", {runargs = {"arg1", "arg2"}})
</code></pre>
<p>如果我们没有配置 runargs 到 <code>add_tests</code>，那么我们也会尝试从被绑定的 target 中，获取 <code>set_runargs</code> 设置的运行参数。</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname")
    set_runargs("arg1", "arg2")
</code></pre>
<h5 id="">配置运行目录</h5>
<p>我们也可以通过 rundir 设置测试运行的当前工作目录，例如：</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname", {rundir = os.projectdir()})
</code></pre>
<p>如果我们没有配置 rundir 到 <code>add_tests</code>，那么我们也会尝试从被绑定的 target 中，获取 <code>set_rundir</code> 设置的运行目录。</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname")
    set_rundir("$(projectdir)")
</code></pre>
<h5 id="">配置运行环境</h5>
<p>我们也可以通过 runenvs 设置一些运行时候的环境变量，例如：</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname", {runenvs = {LD_LIBRARY_PATH = "/lib"}})
</code></pre>
<p>如果我们没有配置 runenvs 到 <code>add_tests</code>，那么我们也会尝试从被绑定的 target 中，获取 <code>add_runenvs</code> 设置的运行环境。</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname")
    add_runenvs("LD_LIBRARY_PATH", "/lib")
</code></pre>
<h5 id="">匹配输出结果</h5>
<p>默认情况下，<code>xmake test</code> 会根据测试运行的退出代码是否为 0，来判断是否测试通过。</p>
<p>当然，我们也可以通过配置测试运行的输出结果是否满足我们的指定的匹配模式，来判断是否测试通过。</p>
<p>主要通过这两个参数控制：</p>
<table>
<thead>
<tr>
<th>参数</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>pass_outputs</td>
<td>如果输出匹配，则测试通过</td>
</tr>
<tr>
<td>fail_outputs</td>
<td>如果输出匹配，则测试失败</td>
</tr>
</tbody>
</table>
<p>传入 <code>pass_outputs</code> 和 <code>fail_outputs</code> 的是一个 lua 匹配模式的列表，但模式稍微做了一些简化，比如对 <code>*</code> 的处理。</p>
<p>如果要匹配成功，则测试通过，可以这么配置：</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname1", {pass_outputs = "hello"})
    add_tests("testname2", {pass_outputs = "hello *"})
    add_tests("testname3", {pass_outputs = {"hello", "hello *"}})
</code></pre>
<p>如果要匹配成功，则测试失败，可以这么配置：</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname1", {fail_outputs = "hello"})
    add_tests("testname2", {fail_outputs = "hello *"})
    add_tests("testname3", {fail_outputs = {"hello", "hello *"}})
</code></pre>
<p>我们也可以同时配置它们：</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname", {pass_outputs = "foo", fail_outputs = "hello"})
</code></pre>
<p>由于一些测试输出的结果，尾部会有一些换行什么的空白字符，干扰匹配模式，我们可以再配置 <code>trim_output = true</code>，先截断空白字符后，再做匹配。</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname", {trim_output = true, pass_outputs = "foo", fail_outputs = "hello"})
</code></pre>
<p>我们还可以配置 <code>{plain = true}</code> 是禁用 lua 模式匹配，仅仅做最基础的平坦文本匹配。</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname", {plain = true, pass_outputs = "foo", fail_outputs = "hello"})
</code></pre>
<h5 id="">配置测试组</h5>
<p>我们也可以通过 <code>group = "foo"</code> 来配置一个测试组，进行分组测试：</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname1", {group = "foo"})
    add_tests("testname2", {group = "foo"})
    add_tests("testname3", {group = "bar"})
    add_tests("testname4", {group = "bae"})
</code></pre>
<p>其中 testname1/testname2 是一个组 foo，另外两个是在另外一个组。</p>
<p>然后，我们就可以使用 <code>xmake test -g groupname</code> 来进行分组测试了。</p>
<pre><code class="lang-bash">$ xmake test -g "foo"
$ xmake test -g "foo*"
</code></pre>
<p>!> 运行分组，也是支持模式匹配的。</p>
<p>另外，如果没有设置 <code>group</code> 参数给 <code>add_tests</code>，我们也可以默认获取绑定到 target 的组名。</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname")
    set_group("foo")
</code></pre>
<h5 id="">自定义测试脚本</h5>
<p>我们还新增了 <code>before_test</code>, <code>on_test</code> 和 <code>after_test</code> 配置脚本，用户可以在 rule 和 target 域，自定义配置它们实现定制化的测试执行。</p>
<pre><code class="lang-lua">target("test")
     on_test(function (target, opt)
        print(opt.name, opt.runenvs, opt.runargs, opt.pass_outputs)

        -- do test
        -- ...

        -- passed
        return true

        -- failied
        return false, errors
     end)
</code></pre>
<p>其中，opt 里面可以获取到所有传入 <code>add_tests</code> 的参数，我们在 on_test 里面自定义测试逻辑，然后返回 true 就是测试通过，返回 false 就是测试失败，然后继续返回测试失败的错误信息。</p>
<h5 id="">自动化构建</h5>
<p>由于测试目标在正常开发构建阶段，通常是不需要被构建的，因此我们会设置 <code>set_default(false)</code>。</p>
<pre><code class="lang-lua">target("test")
    add_tests("testname")
    set_default(false)
</code></pre>
<p>但是运行 <code>xmake test</code> 进行测试时候，这些测试对应的 target 还是会被自动构建，确保能够被运行。</p>
<pre><code class="lang-bash">$ xmake test
[ 25%]: cache compiling.release src/main.cpp
[ 50%]: linking.release test
running tests ...
[100%]: test/testname .................................... passed 6.000s

100% tests passed, 0 tests failed out of 1, spent 0.006s
</code></pre>
<h5 id="">首次测试失败就终止</h5>
<p>默认情况下，<code>xmake test</code> 会等到所有测试都运行完，不管里面有多少是没通过的。</p>
<p>而有时候，我们想在第一个测试没通过，就直接中断测试，那么我们可以通过下面的配置启用：</p>
<pre><code class="lang-lua">set_policy("test.stop_on_first_failure", true)
</code></pre>
<h5 id="0">测试失败返回0</h5>
<p>默认情况下，只要有一个测试没通过，等到 <code>xmake test</code> 运行完成，它都会返回非0退出代码，这对于一些 CI 环境非常有用，可以中断 CI 的其他脚本继续运行。</p>
<p>然后触发信号告诉 CI，我们需要生成测试报告和告警了。</p>
<p>然后，如果我们想要压制这种行为，可以强制将 <code>xmake test</code> 的退出代码总是设置成 0。</p>
<pre><code class="lang-lua">set_policy("test.return_zero_on_failure", true)
</code></pre>
<h5 id="">仅仅测试编译</h5>
<p>有时候，我们仅仅想要测试代码是否通过编译，或者没有通过编译，不需要运行它们，那么可以通过配置 <code>build_should_pass</code> 和 <code>build_should_fail</code> 来实现。</p>
<pre><code class="lang-lua">target("test_10")
    set_kind("binary")
    set_default(false)
    add_files("src/compile.cpp")
    add_tests("compile_fail", {build_should_fail = true})

target("test_11")
    set_kind("binary")
    set_default(false)
    add_files("src/compile.cpp")
    add_tests("compile_pass", {build_should_pass = true})
</code></pre>
<p>这通常用于一些测试代码中带有 <code>static_assert</code> 的场景，例如：</p>
<pre><code class="lang-c++">template <typename T>
bool foo(T val) {
  if constexpr (std::is_same_v<T, int>) {
    printf("int!\n");
  } else if constexpr (std::is_same_v<T, float>) {
    printf("float!\n");
  } else {
    static_assert(false, "unsupported type");
  }
}

int main(int, char**) {
  foo("BAD");
  return 0;
}
</code></pre>
<h5 id="">配置额外的代码编译</h5>
<p>我们还可以在配置测试用例的时候，对每个测试配置额外需要编译的代码，以及一些宏定义，实现内联测试。</p>
<p>xmake 会为每个测试单独编译一个独立的可执行程序去运行它，但这并不会影响到 target 在生产环境的编译结果。</p>
<pre><code class="lang-lua">target("test_13")
    set_kind("binary")
    set_default(false)
    add_files("src/test_1.cpp")
    add_tests("stub_1", {files = "tests/stub_1.cpp", defines = "STUB_1"})

target("test_14")
    set_kind("binary")
    set_default(false)
    add_files("src/test_2.cpp")
    add_tests("stub_2", {files = "tests/stub_2.cpp", defines = "STUB_2"})

target("test_15")
    set_kind("binary")
    set_default(false)
    add_files("src/test_1.cpp")
    add_tests("stub_n", {files = "tests/stub_n*.cpp", defines = "STUB_N"})
</code></pre>
<p>以 doctest 为例，我们可以在不修改任何 main.cpp 的情况下，外置单元测试：</p>
<pre><code class="lang-lua">add_rules("mode.debug", "mode.release")

add_requires("doctest")

target("doctest")
    set_kind("binary")
    add_files("src/*.cpp")
    for _, testfile in ipairs(os.files("tests/*.cpp")) do
        add_tests(path.basename(testfile), {
            files = testfile,
            remove_files = "src/main.cpp",
            languages = "c++11",
            packages = "doctest",
            defines = "DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"})
    end
</code></pre>
<p>定义 DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 会引入额外的 main 入口函数，因此我们需要配置 remove_files 去移除已有的 main.cpp 文件。</p>
<p>运行效果如下：</p>
<pre><code class="lang-bash">ruki-2:doctest ruki$ xmake test
running tests ...
[ 50%]: doctest/test_1 .................................... failed 0.009s
[100%]: doctest/test_2 .................................... passed 0.009s

50% tests passed, 1 tests failed out of 2, spent 0.019s
ruki-2:doctest ruki$ xmake test -v
running tests ...
[ 50%]: doctest/test_1 .................................... failed 0.026s
[doctest] doctest version is "2.4.11"
[doctest] run with "--help" for options
===============================================================================
tests/test_1.cpp:7:
TEST CASE:  testing the factorial function

tests/test_1.cpp:8: ERROR: CHECK( factorial(1) == 10 ) is NOT correct!
  values: CHECK( 1 == 10 )

===============================================================================
[doctest] test cases: 1 | 0 passed | 1 failed | 0 skipped
[doctest] assertions: 4 | 3 passed | 1 failed |
[doctest] Status: FAILURE!

run failed, exit code: 1
[100%]: doctest/test_2 .................................... passed 0.010s

50% tests passed, 1 tests failed out of 2, spent 0.038s
</code></pre>
<h5 id="">测试动态库</h5>
<p>通常，<code>add_tests</code> 仅用于对可执行程序进行运行测试，运行动态库需要有一个额外的 main 主入口，因此我们需要额外配置一个可执行程序去加载它，例如：</p>
<pre><code class="lang-lua">
target("doctest_shared")
    set_kind("shared")
    add_files("src/foo.cpp")
    for _, testfile in ipairs(os.files("tests/*.cpp")) do
        add_tests(path.basename(testfile), {
            kind = "binary",
            files = testfile,
            languages = "c++11",
            packages = "doctest",
            defines = "DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"})
    end
</code></pre>
<p>通过 <code>kind = "binary"</code> 可以将每个单元测试改为 binary 可执行程序，并通过 DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 引入 main 入口函数。</p>
<p>这样就能实现动态库目标中外置可运行的单元测试。</p>
<h5 id="">配置运行超时</h5>
<p>如果一些测试程序长时间运行不退出，就会卡住，我们可以通过配置超时时间，强制退出，并返回失败。</p>
<pre><code class="lang-lua">target("test_timeout")
    set_kind("binary")
    set_default(false)
    add_files("src/run_timeout.cpp")
    add_tests("run_timeout", {run_timeout = 1000})
</code></pre>
<pre><code class="lang-bash">$ xmake test
[100%]: test_timeout/run_timeout .................................... failed 1.006s
run failed, exit code: -1, exit error: wait process timeout
</code></pre>
</article>
</body>
</html>